├── .github ├── ISSUE_TEMPLATE │ ├── 1-bug-report.yml │ ├── 2-improvement.yml │ ├── 3-feature-request.yml │ └── config.yml └── workflows │ ├── check-signed-commits.yml │ ├── ok-to-test.yml │ └── validate.yml ├── .gitignore ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── example ├── README.md ├── example.py ├── file.txt └── file2.txt ├── setup.py ├── src ├── onepassword │ ├── __init__.py │ ├── build_number.py │ ├── client.py │ ├── core.py │ ├── defaults.py │ ├── errors.py │ ├── items.py │ ├── items_files.py │ ├── items_shares.py │ ├── lib │ │ ├── aarch64 │ │ │ ├── libop_uniffi_core.dylib │ │ │ ├── libop_uniffi_core.so │ │ │ └── op_uniffi_core.py │ │ └── x86_64 │ │ │ ├── libop_uniffi_core.dylib │ │ │ ├── libop_uniffi_core.so │ │ │ ├── op_uniffi_core.dll │ │ │ └── op_uniffi_core.py │ ├── secrets.py │ ├── test_client.py │ ├── types.py │ └── vaults.py └── release │ ├── README.md │ ├── RELEASE-NOTES │ ├── scripts │ ├── build-wheels.sh │ ├── prep-release.sh │ └── release.sh │ └── templates │ ├── build_number.tpl.py │ └── version.tpl.py └── version.py /.github/ISSUE_TEMPLATE/1-bug-report.yml: -------------------------------------------------------------------------------- 1 | name: "🐛 Bug Report" 2 | description: Something isn't working as expected. 3 | labels: ["bug"] 4 | body: 5 | - type: textarea 6 | id: scenario 7 | attributes: 8 | label: Scenario & Reproduction Steps 9 | description: When do you encounter this problem? 10 | placeholder: "Please share as much context as you can about when you encounter this problem. If possible, sharing the steps to reproduce is immensely helpful." 11 | validations: 12 | required: true 13 | - type: textarea 14 | id: actual 15 | attributes: 16 | label: Actual Behavior 17 | description: What is happening? 18 | placeholder: "Please tell us about the problem you're encountering. e.g. an error you're encountering or an unexpected return value" 19 | validations: 20 | required: true 21 | - type: textarea 22 | id: expected 23 | attributes: 24 | label: Expected Behavior 25 | description: What would you have expected happened instead? 26 | placeholder: "Please share what you had expected to happen. How should this have behaved?" 27 | - type: input 28 | id: version 29 | attributes: 30 | label: SDK version 31 | description: "You can find the version you're using by running `npm list @1password/sdk`." 32 | - type: textarea 33 | id: info 34 | attributes: 35 | label: Additional information 36 | description: Any additional information that's relevant to add? 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2-improvement.yml: -------------------------------------------------------------------------------- 1 | name: "↗️ Improvement" 2 | description: Something works but can be made better. 3 | labels: ["improvement"] 4 | body: 5 | - type: textarea 6 | id: current 7 | attributes: 8 | label: Current Behavior 9 | description: How does this currently work? 10 | placeholder: "Please tell us what you're currently doing and what hurdles you're running into with this." 11 | validations: 12 | required: true 13 | - type: textarea 14 | id: desired 15 | attributes: 16 | label: Desired Behavior 17 | description: How would you prefer for this to work? 18 | placeholder: "Please share how you'd prefer for this to work." 19 | - type: textarea 20 | id: value 21 | attributes: 22 | label: Benefits & Value 23 | description: What is better about the new behavior? How will this help you? 24 | placeholder: "Please share what benefits you'd like to get out of this improvement. What would you use this for? How does that improve with this change? Why should this change be made?" 25 | - type: textarea 26 | id: info 27 | attributes: 28 | label: Additional information 29 | description: Any additional information that's relevant to add? 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/3-feature-request.yml: -------------------------------------------------------------------------------- 1 | name: "✨ Feature request" 2 | description: I'd like to request new functionality. 3 | labels: ["feature-request"] 4 | body: 5 | - type: textarea 6 | id: usecase 7 | attributes: 8 | label: Use Case 9 | description: What are you trying to achieve? 10 | placeholder: "Tell us about the problem you're trying to solve. The more context you add, the better we can align a solution with your problem." 11 | validations: 12 | required: true 13 | - type: textarea 14 | id: requirements 15 | attributes: 16 | label: Requirements and desired behavior 17 | description: What should the SDK do? 18 | placeholder: If you already have an idea for what you'd like to be available in the SDK to solve your problem, feel free to share that here. How would you expect this to behave? 19 | - type: textarea 20 | id: info 21 | attributes: 22 | label: Additional information 23 | description: Any additional information that's relevant to add? 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: "💬 Chat with us on Slack" 4 | url: https://developer.1password.com/joinslack 5 | about: Chat with us about SDKs in our Developer Slack workspace. 6 | - name: "❓ General 1Password questions" 7 | url: https://1password.community 8 | about: I have a question about 1Password that's not directly related to SDKs. 9 | -------------------------------------------------------------------------------- /.github/workflows/check-signed-commits.yml: -------------------------------------------------------------------------------- 1 | name: Check signed commits in PR 2 | on: pull_request_target 3 | 4 | jobs: 5 | build: 6 | name: Check signed commits in PR 7 | permissions: 8 | contents: read 9 | pull-requests: write 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Check signed commits in PR 13 | uses: 1Password/check-signed-commits-action@main 14 | -------------------------------------------------------------------------------- /.github/workflows/ok-to-test.yml: -------------------------------------------------------------------------------- 1 | # If someone with write access comments "/ok-to-test" on a pull request, emit a repository_dispatch event 2 | name: Ok To Test 3 | 4 | on: 5 | issue_comment: 6 | types: [created] 7 | 8 | jobs: 9 | ok-to-test: 10 | runs-on: ubuntu-latest 11 | # required permissions for adding reactions to the pull request comments 12 | permissions: 13 | pull-requests: write 14 | # Only run for PRs, not issue comments 15 | if: ${{ github.event.issue.pull_request }} 16 | steps: 17 | - name: Slash Command Dispatch 18 | uses: peter-evans/slash-command-dispatch@v3 19 | with: 20 | token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} 21 | reaction-token: ${{ secrets.GITHUB_TOKEN }} 22 | issue-type: pull-request 23 | commands: ok-to-test 24 | # The repository permission level required by the user to dispatch commands. Only allows 1Password collaborators to run this. 25 | permission: write 26 | -------------------------------------------------------------------------------- /.github/workflows/validate.yml: -------------------------------------------------------------------------------- 1 | # This workflow builds, tests, and checks linting for the 1Password Python SDK. 2 | name: Validate 3 | 4 | on: 5 | push: 6 | paths-ignore: 7 | - '**.md' 8 | pull_request: 9 | paths-ignore: 10 | - '**.md' 11 | repository_dispatch: 12 | types: [ ok-to-test-command ] 13 | 14 | jobs: 15 | 16 | test-trusted: 17 | # actions that are trusted by default must only be opened from within the repo, and skipped for forks because they'll fail there 18 | if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository 19 | strategy: 20 | matrix: 21 | os: [ubuntu-latest, windows-latest, macos-latest] 22 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 23 | runs-on: ${{ matrix.os }} 24 | steps: 25 | - uses: actions/checkout@v3 26 | - name: Set up Python 27 | uses: actions/setup-python@v4 28 | with: 29 | python-version: ${{ matrix.python-version }} 30 | - name: Integration Test 31 | env: 32 | OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.TEST_SERVICE_ACCOUNT_TOKEN }} 33 | run: | 34 | pip install pytest && 35 | pip install pytest-asyncio && 36 | pip install pydantic && 37 | python -m pytest src/onepassword/test_client.py 38 | - name: Example Test 39 | if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.9' 40 | env: 41 | OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.EXAMPLE_TESTS_OP_SERVICE_ACCOUNT_TOKEN }} 42 | OP_VAULT_ID: ${{ secrets.EXAMPLE_TESTS_OP_VAULT_ID }} 43 | run: | 44 | pip install cryptography && 45 | pip install . && 46 | python example/example.py 47 | lint: 48 | name: Lint 49 | runs-on: ubuntu-latest 50 | steps: 51 | - uses: actions/checkout@v3 52 | 53 | - name: Set up Python 54 | uses: actions/setup-python@v4 55 | with: 56 | python-version: '3.x' 57 | 58 | - name: Lint with Ruff 59 | run: | 60 | pip install ruff 61 | ruff check --output-format=github --exclude=src/onepassword/lib/,example/ . 62 | continue-on-error: true 63 | 64 | # This action is called by the /ok-to-test command, once the forked PR's code has been security reviewed. 65 | # It will checkout the forked (and now trusted) code and it will run the integration tests on it. 66 | # If the tests are successful this action will proceed to update the status of the forked PR integration check. 67 | integration-test-fork: 68 | # required permissions for updating the status of the pull request checks 69 | permissions: 70 | pull-requests: write 71 | checks: write 72 | strategy: 73 | matrix: 74 | os: [ubuntu-latest, windows-latest, macos-latest] 75 | runs-on: ${{ matrix.os }} 76 | if: | 77 | github.event_name == 'repository_dispatch' && 78 | github.event.client_payload.slash_command.args.named.sha != '' && 79 | contains( 80 | github.event.client_payload.pull_request.head.sha, 81 | github.event.client_payload.slash_command.args.named.sha 82 | ) 83 | steps: 84 | 85 | # Check out merge commit 86 | - name: Fork based /ok-to-test checkout 87 | uses: actions/checkout@v4 88 | with: 89 | ref: ${{ github.event.client_payload.pull_request.head.sha }} 90 | 91 | - name: Set up Python 92 | uses: actions/setup-python@v4 93 | with: 94 | python-version: '3.x' 95 | 96 | - name: Integration Test 97 | env: 98 | OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.TEST_SERVICE_ACCOUNT_TOKEN }} 99 | run: | 100 | pip install pytest && 101 | pip install pytest-asyncio && 102 | pip install pydantic && 103 | python -m pytest src/onepassword/test_client.py 104 | 105 | - name: Example Test 106 | if: matrix.os == 'ubuntu-latest' 107 | env: 108 | OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.TEST_SERVICE_ACCOUNT_TOKEN }} 109 | OP_VAULT_ID: ${{ secrets.TEST_SERVICE_ACCOUNT_VAULT_ID }} 110 | run: | 111 | pip install . && 112 | python example/example.py 113 | 114 | # Update check run called "integration-fork" on the forked PR 115 | - uses: actions/github-script@v6 116 | id: update-check-run 117 | if: ${{ always() }} 118 | env: 119 | job: ${{ github.job }} 120 | ref: ${{ github.event.client_payload.pull_request.head.sha }} 121 | # Conveniently, job.status maps to https://developer.github.com/v3/checks/runs/#update-a-check-run 122 | conclusion: ${{ job.status }} 123 | with: 124 | github-token: ${{ secrets.GITHUB_TOKEN }} 125 | script: | 126 | const { data: checks } = await github.rest.checks.listForRef({ 127 | ...context.repo, 128 | ref: process.env.ref 129 | }); 130 | 131 | const check = checks.check_runs.filter(c => c.name === process.env.job); 132 | 133 | const { data: result } = await github.rest.checks.update({ 134 | ...context.repo, 135 | check_run_id: check[0].id, 136 | status: 'completed', 137 | conclusion: process.env.conclusion 138 | }); 139 | 140 | return result; 141 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pytest_cache 2 | .idea 3 | *__pycache__ 4 | dist/ 5 | .DS_Store 6 | build/ 7 | .python-version 8 | onepassword_sdk.egg-info/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 1Password 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include version.py 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PYTHON_VERSIONS := 3.9 3.10 3.11 3.12 3.13 2 | 3 | release: 4 | src/release/scripts/release.sh 5 | 6 | prep-release: 7 | src/release/scripts/prep-release.sh 8 | 9 | build-wheels: 10 | src/release/scripts/build-wheels.sh $(PYTHON_VERSIONS) 11 | 12 | release/install-dependencies: 13 | # Install latest version of pyenv if not already installed 14 | brew install pyenv 15 | 16 | # Install all the python versions we support in one line 17 | pyenv install --skip-existing $(PYTHON_VERSIONS) 18 | 19 | # Set pyenv local and install dependencies for each version 20 | for version in $(PYTHON_VERSIONS); do \ 21 | pyenv local $$version; \ 22 | pyenv exec pip3 install wheel setuptools build --break-system-packages; \ 23 | done 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

1Password Python SDK

4 | 5 |

6 | 7 |

8 |

Build integrations that programmatically access your secrets in 1Password.

9 |

10 | 11 |

12 | Documentation | Examples 13 |
14 | 15 | --- 16 | 17 | ## Requirements 18 | 19 | The 1Password Python SDK is compatible with: 20 | 21 | - `python` 3.9 or later 22 | - `libssl` 3 23 | - `glibc` 2.32 or later 24 | 25 | If you're running a Linux distribution that still uses `libssl` version 1.1.1, such as Debian 11 or Ubuntu 20.04, you'll need to update to a later version of Linux or install the required dependencies. 26 | 27 | ## 🚀 Get started 28 | 29 | To use the 1Password Python SDK in your project: 30 | 31 | 1. [Create a service account](https://my.1password.com/developer-tools/infrastructure-secrets/serviceaccount/) and give it the appropriate permissions in the vaults where the items you want to use with the SDK are saved. 32 | 2. Provision your service account token. We recommend provisioning your token from the environment. For example, to export your token to the `OP_SERVICE_ACCOUNT_TOKEN` environment variable: 33 | 34 | **macOS or Linux** 35 | 36 | ```bash 37 | export OP_SERVICE_ACCOUNT_TOKEN= 38 | ``` 39 | 40 | **Windows** 41 | 42 | ```powershell 43 | $Env:OP_SERVICE_ACCOUNT_TOKEN = "" 44 | ``` 45 | 46 | 3. Install the 1Password Python SDK in your project: 47 | 48 | ```bash 49 | pip install onepassword-sdk 50 | ``` 51 | 52 | 4. Use the Python SDK in your project: 53 | 54 | ```python 55 | import asyncio 56 | import os 57 | from onepassword.client import Client 58 | 59 | async def main(): 60 | # Gets your service account token from the OP_SERVICE_ACCOUNT_TOKEN environment variable. 61 | token = os.getenv("OP_SERVICE_ACCOUNT_TOKEN") 62 | 63 | # Connects to 1Password. Fill in your own integration name and version. 64 | client = await Client.authenticate(auth=token, integration_name="My 1Password Integration", integration_version="v1.0.0") 65 | 66 | # Retrieves a secret from 1Password. Takes a secret reference as input and returns the secret to which it points. 67 | value = await client.secrets.resolve("op://vault/item/field") 68 | # use value here 69 | 70 | if __name__ == '__main__': 71 | asyncio.run(main()) 72 | 73 | ``` 74 | 75 | Make sure to use [secret reference URIs](https://developer.1password.com/docs/cli/secret-reference-syntax/) with the syntax `op://vault/item/field` to securely load secrets from 1Password into your code. 76 | 77 | ## Supported functionality 78 | 79 | 1Password SDKs are in active development. We're keen to hear what you'd like to see next. Let us know by [upvoting](https://github.com/1Password/onepassword-sdk-python/issues) or [filing](https://github.com/1Password/onepassword-sdk-python/issues/new/choose) an issue. 80 | 81 | ### Item management 82 | 83 | Operations: 84 | 85 | - [x] [Retrieve secrets](https://developer.1password.com/docs/sdks/load-secrets) 86 | - [x] [Retrieve items](https://developer.1password.com/docs/sdks/manage-items#get-an-item) 87 | - [x] [Create items](https://developer.1password.com/docs/sdks/manage-items#create-an-item) 88 | - [x] [Update items](https://developer.1password.com/docs/sdks/manage-items#update-an-item) 89 | - [x] [Delete items](https://developer.1password.com/docs/sdks/manage-items#delete-an-item) 90 | - [x] [Archive items](https://developer.1password.com/docs/sdks/manage-items/#archive-an-item) 91 | - [x] [List items](https://developer.1password.com/docs/sdks/list-vaults-items/) 92 | - [x] [Share items](https://developer.1password.com/docs/sdks/share-items) 93 | - [x] [Generate PIN, random and memorable passwords](https://developer.1password.com/docs/sdks/manage-items#generate-a-password) 94 | 95 | Field types: 96 | - [x] API Keys 97 | - [x] Passwords 98 | - [x] Concealed fields 99 | - [x] Text fields 100 | - [x] Notes 101 | - [x] SSH private keys, public keys, fingerprint and key type 102 | - [x] One-time passwords 103 | - [x] URLs 104 | - [x] Websites (used to suggest and autofill logins) 105 | - [x] Phone numbers 106 | - [x] Credit card types 107 | - [x] Credit card numbers 108 | - [x] Emails 109 | - [x] References to other items 110 | - [x] Address 111 | - [x] Date 112 | - [x] MM/YY 113 | - [x] Files attachments and Document items 114 | - [x] Menu 115 | 116 | ### Vault management 117 | - [ ] Retrieve vaults 118 | - [ ] Create vaults ([#36](https://github.com/1Password/onepassword-sdk-python/issues/36)) 119 | - [ ] Update vaults 120 | - [ ] Delete vaults 121 | - [x] [List vaults](https://developer.1password.com/docs/sdks/list-vaults-items/) 122 | 123 | ### User & access management 124 | - [ ] Provision users 125 | - [ ] Retrieve users 126 | - [ ] List users 127 | - [ ] Suspend users 128 | - [ ] Create groups 129 | - [ ] Update group membership 130 | - [ ] Update vault access & permissions 131 | 132 | ### Compliance & reporting 133 | - [ ] Watchtower insights 134 | - [ ] Travel mode 135 | - [ ] Events. For now, use [1Password Events Reporting API](https://developer.1password.com/docs/events-api/) directly. 136 | 137 | ### Authentication 138 | 139 | - [x] [1Password Service Accounts](https://developer.1password.com/docs/service-accounts/get-started/) 140 | - [ ] User authentication 141 | - [ ] 1Password Connect. For now, use [1Password/connect-sdk-python](https://github.com/1Password/connect-sdk-python). 142 | 143 | ## 📖 Learn more 144 | 145 | - [Load secrets with 1Password SDKs](https://developer.1password.com/docs/sdks/load-secrets) 146 | - [Manage items with 1Password SDKs](https://developer.1password.com/docs/sdks/manage-items) 147 | - [List vaults and items with 1Password SDKs](https://developer.1password.com/docs/sdks/list-vaults-items) 148 | - [1Password SDK concepts](https://developer.1password.com/docs/sdks/concepts) 149 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | This folder contains a code snippet demonstrating how to use the 1Password Python SDK for performing various operations on 1Password vaults and items. Specifically, the example showcases how to: 3 | 4 | - Authenticate with the 1Password API using a service account token. 5 | - List available vaults and items within those vaults. 6 | - Retrieve a specific secret and resolve a one-time password (TOTP). 7 | - Create a new item in a vault with multiple fields and tags. 8 | - Update an existing item by modifying its fields and adding a new website. 9 | - Generate different types of passwords (PIN, memorable, and random). 10 | - Share an item with valid recipients and create a shareable link. 11 | - Archive or delete items from the vault. 12 | - Create and manage SSH key items. 13 | - Create and manage document items, including replacing and reading documents. 14 | - Create and manage file field items by attaching and deleting files. 15 | 16 | ## Prerequisites 17 | 18 | 1. Clone the repository and follow the steps to [get started](https://github.com/1Password/onepassword-sdk-python/blob/main/README.md). 19 | 2. Ensure that you have a valid service account token by exporting it as an environment variable: 20 | ```bash 21 | export OP_SERVICE_ACCOUNT_TOKEN="" 22 | ``` 23 | 3. Export the vault UUID you wish to interact with as an environment variable: 24 | ```bash 25 | export OP_VAULT_ID="" 26 | ``` 27 | 28 | ## How to Run 29 | 30 | To run the example file, navigate to project root directory and run: 31 | ```bash 32 | python example/example.py 33 | ``` 34 | 35 | ## Terminal Output 36 | 37 | When running the example, the terminal will display: 38 | 39 | - A list of vaults and items. 40 | - Retrieved secrets and TOTP codes. 41 | - Details of newly created and updated items. 42 | - Generated passwords (PIN, memorable, random). 43 | - A shareable link for shared items. 44 | - SSH key attributes like public key and fingerprint. 45 | - Document content after replacing the file. 46 | - A list of file field items and file deletions. 47 | 48 | These outputs show the results of vault and item operations, password generation, item sharing, and management of SSH and document items. 49 | -------------------------------------------------------------------------------- /example/example.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import os 3 | from pathlib import Path 4 | 5 | # [developer-docs.sdk.python.sdk-import]-start 6 | from onepassword import * 7 | 8 | # [developer-docs.sdk.python.sdk-import]-end 9 | from cryptography.hazmat.primitives.asymmetric import rsa 10 | from cryptography.hazmat.primitives import serialization 11 | 12 | 13 | async def main(): 14 | # [developer-docs.sdk.python.client-initialization]-start 15 | # Gets your service account token from the OP_SERVICE_ACCOUNT_TOKEN environment variable. 16 | token = os.getenv("OP_SERVICE_ACCOUNT_TOKEN") 17 | 18 | # Connects to 1Password. 19 | client = await Client.authenticate( 20 | auth=token, 21 | # Set the following to your own integration name and version. 22 | integration_name="My 1Password Integration", 23 | integration_version="v1.0.0", 24 | ) 25 | # [developer-docs.sdk.python.client-initialization]-end 26 | 27 | # [developer-docs.sdk.python.list-vaults]-start 28 | vaults = await client.vaults.list() 29 | for vault in vaults: 30 | print(vault.title) 31 | # [developer-docs.sdk.python.list-vaults]-end 32 | 33 | # [developer-docs.sdk.python.list-items]-start 34 | overviews = await client.items.list(vault.id) 35 | for overview in overviews: 36 | print(overview.title) 37 | # [developer-docs.sdk.python.list-items]-end 38 | # [developer-docs.sdk.python.use-item-filters]-start 39 | archived_overviews = await client.items.list( 40 | vault.id, 41 | ItemListFilterByState( 42 | content=ItemListFilterByStateInner(active=False, archived=True) 43 | ), 44 | ) 45 | for overview in archived_overviews: 46 | print(overview.title) 47 | # [developer-docs.sdk.python.use-item-filters]-end 48 | # [developer-docs.sdk.python.validate-secret-reference]-start 49 | # Validate secret reference to ensure no syntax errors 50 | try: 51 | Secrets.validate_secret_reference("op://vault/item/field") 52 | except Exception as error: 53 | print(error) 54 | # [developer-docs.sdk.python.validate-secret-reference]-end 55 | 56 | vault_id= os.getenv("OP_VAULT_ID") 57 | if vault_id is None: 58 | raise Exception("OP_VAULT_ID environment variable is not set") 59 | 60 | # [developer-docs.sdk.python.create-item]-start 61 | # Create an Item and add it to your vault. 62 | to_create = ItemCreateParams( 63 | title="MyName", 64 | category=ItemCategory.LOGIN, 65 | vault_id=vault_id, 66 | fields=[ 67 | ItemField( 68 | id="username", 69 | title="username", 70 | field_type=ItemFieldType.TEXT, 71 | value="mynameisjeff", 72 | ), 73 | ItemField( 74 | id="password", 75 | title="password", 76 | field_type=ItemFieldType.CONCEALED, 77 | value="jeff", 78 | ), 79 | ItemField( 80 | id="onetimepassword", 81 | title="one-time-password", 82 | field_type=ItemFieldType.TOTP, 83 | section_id="totpsection", 84 | value="otpauth://totp/my-example-otp?secret=jncrjgbdjnrncbjsr&issuer=1Password", 85 | ), 86 | ], 87 | sections=[ 88 | ItemSection(id="", title=""), 89 | ItemSection(id="totpsection", title=""), 90 | ], 91 | tags=["test tag 1", "test tag 2"], 92 | websites=[ 93 | Website( 94 | label="my custom website", 95 | url="https://example.com", 96 | autofill_behavior=AutofillBehavior.NEVER, 97 | ) 98 | ], 99 | ) 100 | created_item = await client.items.create(to_create) 101 | # [developer-docs.sdk.python.create-item]-end 102 | 103 | print(dict(created_item)) 104 | 105 | # [developer-docs.sdk.python.resolve-secret]-start 106 | # Retrieves a secret from 1Password. Takes a secret reference as input and returns the secret to which it points. 107 | value = await client.secrets.resolve(f"op://{created_item.vault_id}/{created_item.id}/username") 108 | print(value) 109 | # [developer-docs.sdk.python.resolve-secret]-end 110 | 111 | # [developer-docs.sdk.python.resolve-totp-code]-start 112 | # Retrieves a secret from 1Password. Takes a secret reference as input and returns the secret to which it points. 113 | code = await client.secrets.resolve( 114 | f"op://{created_item.vault_id}/{created_item.id}/TOTP_onetimepassword?attribute=totp" 115 | ) 116 | print(code) 117 | # [developer-docs.sdk.python.resolve-totp-code]-end 118 | await resolve_all_secrets( 119 | client, created_item.vault_id, created_item.id, "username", "password" 120 | ) 121 | # [developer-docs.sdk.python.get-totp-item-crud]-start 122 | # Fetch a totp code from the item 123 | for f in created_item.fields: 124 | if f.field_type == "Totp": 125 | if f.details.content.error_message is not None: 126 | print(f.details.content.error_message) 127 | else: 128 | print(f.details.content.code) 129 | # [developer-docs.sdk.python.get-totp-item-crud]-end 130 | 131 | # [developer-docs.sdk.python.get-item]-start 132 | # Retrieve an item from your vault. 133 | item = await client.items.get(created_item.vault_id, created_item.id) 134 | # [developer-docs.sdk.python.get-item]-end 135 | 136 | print(dict(item)) 137 | 138 | # [developer-docs.sdk.python.update-item]-start 139 | # Update a field in your item 140 | item.fields[0].value = "new_value" 141 | item.websites.append( 142 | Website( 143 | label="my custom website 2", 144 | url="https://example2.com", 145 | autofill_behavior=AutofillBehavior.NEVER, 146 | ), 147 | ) 148 | updated_item = await client.items.put(item) 149 | # [developer-docs.sdk.python.update-item]-end 150 | 151 | print(dict(updated_item)) 152 | 153 | # [developer-docs.sdk.python.generate-pin-password]-start 154 | pin_password = Secrets.generate_password( 155 | PasswordRecipePin(parameters=PasswordRecipePinInner(length=8)) 156 | ) 157 | print(pin_password) 158 | # [developer-docs.sdk.python.generate-pin-password]-end 159 | 160 | # [developer-docs.sdk.python.generate-memorable-password]-start 161 | memorable_password = Secrets.generate_password( 162 | PasswordRecipeMemorable( 163 | parameters=PasswordRecipeMemorableInner( 164 | separatorType=SeparatorType.UNDERSCORES, 165 | wordListType=WordListType.SYLLABLES, 166 | capitalize=False, 167 | wordCount=3, 168 | ) 169 | ), 170 | ) 171 | print(memorable_password) 172 | # [developer-docs.sdk.python.generate-memorable-password]-end 173 | 174 | # [developer-docs.sdk.python.generate-random-password]-start 175 | random_password = Secrets.generate_password( 176 | PasswordRecipeRandom( 177 | parameters=PasswordRecipeRandomInner( 178 | length=10, 179 | includeDigits=False, 180 | includeSymbols=False, 181 | ) 182 | ), 183 | ) 184 | print(random_password) 185 | # [developer-docs.sdk.python.generate-random-password]-end 186 | 187 | await share_item(client, updated_item.vault_id, updated_item.id) 188 | 189 | await create_ssh_key_item(client, vault_id) 190 | 191 | await create_and_replace_document_item(client, vault_id) 192 | 193 | await create_attach_and_delete_file_field_item(client, vault_id) 194 | 195 | await archive_item(client, updated_item.vault_id, updated_item.id) 196 | 197 | # [developer-docs.sdk.python.delete-item]-start 198 | # Delete a item from your vault. 199 | await client.items.delete(created_item.vault_id, updated_item.id) 200 | # [developer-docs.sdk.python.delete-item]-end 201 | 202 | 203 | async def archive_item(client: Client, vault_id: str, item_id: str): 204 | # [developer-docs.sdk.python.archive-item]-start 205 | # Archive a item from your vault. 206 | await client.items.archive(vault_id, item_id) 207 | # [developer-docs.sdk.python.archive-item]-end 208 | 209 | 210 | async def share_item(client: Client, vault_id: str, item_id: str): 211 | # [developer-docs.sdk.python.item-share-get-item]-start 212 | item = await client.items.get(vault_id, item_id) 213 | print(item) 214 | # [developer-docs.sdk.python.item-share-get-item]-end 215 | 216 | # [developer-docs.sdk.python.item-share-get-account-policy]-start 217 | policy = await client.items.shares.get_account_policy(item.vault_id, item.id) 218 | print(policy) 219 | # [developer-docs.sdk.python.item-share-get-account-policy]-end 220 | 221 | # [developer-docs.sdk.python.item-share-validate-recipients]-start 222 | valid_recipients = await client.items.shares.validate_recipients( 223 | policy, ["agilebits.com"] 224 | ) 225 | 226 | print(valid_recipients) 227 | # [developer-docs.sdk.python.item-share-validate-recipients]-end 228 | 229 | # [developer-docs.sdk.python.item-share-create-share]-start 230 | share_link = await client.items.shares.create( 231 | item, 232 | policy, 233 | ItemShareParams( 234 | recipients=valid_recipients, 235 | expireAfter=ItemShareDuration.ONEHOUR, 236 | oneTimeOnly=False, 237 | ), 238 | ) 239 | 240 | print(share_link) 241 | # [developer-docs.sdk.python.item-share-create-share]-end 242 | 243 | 244 | async def create_ssh_key_item(client: Client, vault_id: str): 245 | # [developer-docs.sdk.python.create-sshkey-item]-start 246 | # Generate a 2048-bit RSA private key 247 | private_key = rsa.generate_private_key( 248 | public_exponent=65537, 249 | key_size=4096, 250 | ) 251 | 252 | # Serialize the private key in PKCS8 format (PEM) 253 | ssh_key_pkcs8_pem = private_key.private_bytes( 254 | encoding=serialization.Encoding.PEM, 255 | format=serialization.PrivateFormat.PKCS8, 256 | encryption_algorithm=serialization.NoEncryption(), 257 | ) 258 | 259 | # Create an Item containing SSH Key and add it to your vault. 260 | to_create = ItemCreateParams( 261 | title="SSH Key Item Created With Python SDK", 262 | category=ItemCategory.SSHKEY, 263 | vault_id=vault_id, 264 | fields=[ 265 | ItemField( 266 | id="private_key", 267 | title="private key", 268 | field_type=ItemFieldType.SSHKEY, 269 | value=ssh_key_pkcs8_pem, 270 | sectionId="", 271 | ), 272 | ], 273 | sections=[ 274 | ItemSection(id="", title=""), 275 | ], 276 | ) 277 | created_item = await client.items.create(to_create) 278 | 279 | print(created_item.fields[0].value) 280 | print(created_item.fields[0].details.content.public_key) 281 | print(created_item.fields[0].details.content.fingerprint) 282 | print(created_item.fields[0].details.content.key_type) 283 | # [developer-docs.sdk.python.create-sshkey-item]-end 284 | await client.items.delete(created_item.vault_id, created_item.id) 285 | 286 | 287 | async def create_and_replace_document_item(client: Client, vault_id: str): 288 | # [developer-docs.sdk.python.create-document-item]-start 289 | # Create a Document Item 290 | to_create = ItemCreateParams( 291 | title="Document Item Created with Python SDK", 292 | category=ItemCategory.DOCUMENT, 293 | vault_id=vault_id, 294 | sections=[ 295 | ItemSection(id="", title=""), 296 | ], 297 | document=DocumentCreateParams( 298 | name="file.txt", content=Path("./example/file.txt").read_bytes() 299 | ), 300 | ) 301 | created_item = await client.items.create(to_create) 302 | # [developer-docs.sdk.python.create-document-item]-end 303 | 304 | # [developer-docs.sdk.python.replace-document-item]-start 305 | # Replace the document in the item 306 | replaced_item = await client.items.files.replace_document( 307 | created_item, 308 | DocumentCreateParams( 309 | name="file2.txt", content=Path("./example/file2.txt").read_bytes() 310 | ), 311 | ) 312 | # [developer-docs.sdk.python.replace-document-item]-end 313 | 314 | # [developer-docs.sdk.python.read-document-item]-start 315 | # Read the document in the item 316 | content = await client.items.files.read( 317 | replaced_item.vault_id, replaced_item.id, replaced_item.document 318 | ) 319 | # [developer-docs.sdk.python.read-document-item]-end 320 | 321 | print(content.decode()) 322 | 323 | await client.items.delete(replaced_item.vault_id, replaced_item.id) 324 | 325 | 326 | async def create_attach_and_delete_file_field_item(client: Client, vault_id: str): 327 | # [developer-docs.sdk.python.create-item-with-file-field]-start 328 | # Create a File Field Item 329 | to_create = ItemCreateParams( 330 | title="FileField Item created with Python SDK", 331 | category=ItemCategory.LOGIN, 332 | vault_id=vault_id, 333 | fields=[ 334 | ItemField( 335 | id="username", 336 | title="username", 337 | field_type=ItemFieldType.TEXT, 338 | value="mynameisjeff", 339 | ), 340 | ItemField( 341 | id="password", 342 | title="password", 343 | field_type=ItemFieldType.CONCEALED, 344 | value="jeff", 345 | ), 346 | ], 347 | sections=[ 348 | ItemSection(id="", title=""), 349 | ], 350 | files=[ 351 | FileCreateParams( 352 | name="file.txt", 353 | content=Path("./example/file.txt").read_bytes(), 354 | sectionId="", 355 | fieldId="file_field", 356 | ) 357 | ], 358 | ) 359 | 360 | created_item = await client.items.create(to_create) 361 | # [developer-docs.sdk.python.create-item-with-file-field]-end 362 | 363 | # [developer-docs.sdk.python.read-file-field]-start 364 | # Read the file field from an item 365 | content = await client.items.files.read( 366 | created_item.vault_id, created_item.id, created_item.files[0].attributes 367 | ) 368 | # [developer-docs.sdk.python.read-file-field]-end 369 | print(content.decode()) 370 | 371 | # [developer-docs.sdk.python.attach-file-field-item]-start 372 | # Attach a file field to the item 373 | attached_item = await client.items.files.attach( 374 | created_item, 375 | FileCreateParams( 376 | name="file2.txt", 377 | content=Path("./example/file2.txt").read_bytes(), 378 | sectionId="", 379 | fieldId="new_file_field", 380 | ), 381 | ) 382 | # [developer-docs.sdk.python.attach-file-field-item]-end 383 | 384 | # [developer-docs.sdk.python.delete-file-field-item]-start 385 | # Delete a file field from an item 386 | deleted_file_item = await client.items.files.delete( 387 | attached_item, 388 | attached_item.files[1].section_id, 389 | attached_item.files[1].field_id, 390 | ) 391 | # [developer-docs.sdk.python.delete-file-field-item]-end 392 | 393 | print(len(deleted_file_item.files)) 394 | 395 | await client.items.delete(deleted_file_item.vault_id, deleted_file_item.id) 396 | 397 | 398 | async def resolve_all_secrets( 399 | client: Client, vault_id: str, item_id: str, field_id: str, field_id2: str 400 | ): 401 | # [developer-docs.sdk.python.resolve-bulk-secret]-start 402 | # Retrieves multiple secrets from 1Password. 403 | secrets = await client.secrets.resolve_all( 404 | [ 405 | f"op://{vault_id}/{item_id}/{field_id}", 406 | f"op://{vault_id}/{item_id}/{field_id2}", 407 | ] 408 | ) 409 | for secret in secrets.individual_responses.values(): 410 | if secret.error is not None: 411 | print(str(secret.error)) 412 | else: 413 | print(secret.content.secret) 414 | # [developer-docs.sdk.python.resolve-bulk-secret]-end 415 | 416 | 417 | def generate_special_item_fields(): 418 | fields = ( 419 | [ 420 | # [developer-docs.sdk.python.address-field-type]-start 421 | ItemField( 422 | id="address", 423 | title="Address", 424 | sectionId="", 425 | field_type=ItemFieldType.ADDRESS, 426 | value="", 427 | details=ItemFieldDetailsAddress( 428 | content=AddressFieldDetails( 429 | street="1234 Main St", 430 | city="San Francisco", 431 | state="CA", 432 | zip="94111", 433 | country="USA", 434 | ), 435 | ), 436 | ), 437 | # [developer-docs.sdk.python.address-field-type]-end 438 | # [developer-docs.sdk.python.date-field-type]-start 439 | ItemField( 440 | id="date", 441 | title="Date", 442 | section_id="", 443 | field_type=ItemFieldType.DATE, 444 | value="1998-03-15", 445 | ), 446 | # [developer-docs.sdk.python.date-field-type]-end 447 | # [developer-docs.sdk.python.month-year-field-type]-start 448 | ItemField( 449 | id="month_year", 450 | title="Month Year", 451 | section_id="", 452 | field_type=ItemFieldType.MONTHYEAR, 453 | value="03/1998", 454 | ), 455 | # [developer-docs.sdk.python.month-year-field-type]-end 456 | # [developer-docs.sdk.python.reference-field-type]-start 457 | ItemField( 458 | id="Reference", 459 | title="Reference", 460 | sectionId="", 461 | field_type=ItemFieldType.REFERENCE, 462 | value="f43hnkatjllm5fsfsmgaqdhv7a", 463 | ), 464 | # [developer-docs.sdk.python.reference-field-type]-end 465 | # [developer-docs.sdk.python.totp-field-type]-start 466 | ItemField( 467 | id="onetimepassword", 468 | title="one-time-password", 469 | section_id="", 470 | field_type=ItemFieldType.TOTP, 471 | value="otpauth://totp/my-example-otp?secret=jncrjgbdjnrncbjsr&issuer=1Password", 472 | ), 473 | # [developer-docs.sdk.python.totp-field-type]-end 474 | ], 475 | ) 476 | 477 | 478 | if __name__ == "__main__": 479 | asyncio.run(main()) 480 | -------------------------------------------------------------------------------- /example/file.txt: -------------------------------------------------------------------------------- 1 | Hello World! -------------------------------------------------------------------------------- /example/file2.txt: -------------------------------------------------------------------------------- 1 | Hello again, world! -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from setuptools import setup, find_packages 3 | from sysconfig import get_platform 4 | from version import SDK_VERSION 5 | import platform 6 | import os 7 | 8 | try: 9 | from wheel.bdist_wheel import bdist_wheel as _bdist_wheel 10 | 11 | class bdist_wheel(_bdist_wheel): 12 | def finalize_options(self): 13 | _bdist_wheel.finalize_options(self) 14 | self.root_is_pure = False 15 | # This platform naming is sufficient for distributing this package via source cloning (e.g. pip + GitHub) since the wheel will be built locally 16 | # for each user's platform: https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/#basic-platform-tags 17 | self.plat_name = get_platform().translate({"-": "_", ".": "_"}) 18 | self.plat_name_supplied = True 19 | except ImportError: 20 | bdist_wheel = None 21 | 22 | 23 | def get_shared_library_data_to_include(): 24 | # Return the correct uniffi C shared library extension for the given platform 25 | include_path = "lib" 26 | machine_type = os.getenv("PYTHON_MACHINE_PLATFORM") or platform.machine().lower() 27 | if machine_type in ["x86_64", "amd64"]: 28 | include_path = os.path.join(include_path, "x86_64") 29 | elif machine_type in ["aarch64", "arm64"]: 30 | include_path = os.path.join(include_path, "aarch64") 31 | 32 | # Map current platform to the correct shared library file name 33 | platform_to_lib = { 34 | "Darwin": "libop_uniffi_core.dylib", 35 | "Linux": "libop_uniffi_core.so", 36 | "Windows": "op_uniffi_core.dll", 37 | } 38 | platform_name = os.getenv("PYTHON_OS_PLATFORM") or platform.system() 39 | c_shared_library_file_name = platform_to_lib.get(platform_name, "") 40 | c_shared_library_file_name = os.path.join(include_path, c_shared_library_file_name) 41 | 42 | uniffi_bindings_file_name = "op_uniffi_core.py" 43 | uniffi_bindings_file_name = os.path.join(include_path, uniffi_bindings_file_name) 44 | 45 | return [c_shared_library_file_name, uniffi_bindings_file_name] 46 | 47 | 48 | setup( 49 | name="onepassword-sdk", 50 | version=SDK_VERSION, 51 | author="1Password", 52 | long_description=(Path(__file__).parent / "README.md").read_text(), 53 | long_description_content_type="text/markdown", 54 | description="The 1Password Python SDK offers programmatic read access to your secrets in 1Password in an interface native to Python.", 55 | url="https://github.com/1Password/onepassword-sdk-python", 56 | packages=find_packages( 57 | where="src", 58 | ), 59 | license="MIT", 60 | license_files="LICENSE", 61 | package_dir={"": "src"}, 62 | python_requires=">=3.9", 63 | classifiers=[ 64 | "Development Status :: 5 - Production/Stable", 65 | "Operating System :: MacOS", 66 | "Operating System :: POSIX :: Linux", 67 | "Operating System :: Microsoft :: Windows", 68 | "Programming Language :: Python :: 3.9", 69 | "Programming Language :: Python :: 3.10", 70 | "Programming Language :: Python :: 3.11", 71 | "Programming Language :: Python :: 3.12", 72 | "Programming Language :: Python :: 3.13", 73 | "License :: OSI Approved :: MIT License", 74 | ], 75 | cmdclass={"bdist_wheel": bdist_wheel}, 76 | package_data={"": get_shared_library_data_to_include()}, 77 | install_requires=[ 78 | "pydantic>=2.5", # Minimum Pydantic version to run the Python SDK 79 | ], 80 | ) 81 | -------------------------------------------------------------------------------- /src/onepassword/__init__.py: -------------------------------------------------------------------------------- 1 | # Code generated by op-codegen - DO NO EDIT MANUALLY 2 | 3 | from .client import Client 4 | from .defaults import DEFAULT_INTEGRATION_NAME, DEFAULT_INTEGRATION_VERSION 5 | from .types import * # noqa F403 6 | from .errors import * # noqa F403 7 | from .secrets import Secrets 8 | from .items import Items 9 | from .vaults import Vaults 10 | 11 | 12 | import sys 13 | import inspect 14 | import typing 15 | 16 | __all__ = [ 17 | "Client", 18 | "Secrets", 19 | "Items", 20 | "Vaults", 21 | "DEFAULT_INTEGRATION_NAME", 22 | "DEFAULT_INTEGRATION_VERSION", 23 | ] 24 | 25 | for name, obj in inspect.getmembers(sys.modules["onepassword.types"]): 26 | # Add all classes and instances of typing.Literal defined in types.py. 27 | if ( 28 | ( 29 | inspect.isclass(obj) 30 | and inspect.getmodule(obj) == sys.modules["onepassword.types"] 31 | ) 32 | or isinstance(obj, int) 33 | or type(obj) == typing._LiteralGenericAlias 34 | ): 35 | __all__.append(name) 36 | 37 | for name, obj in inspect.getmembers(sys.modules["onepassword.errors"]): 38 | # Add all classes defined in errors.py. 39 | if ( 40 | inspect.isclass(obj) 41 | and inspect.getmodule(obj) == sys.modules["onepassword.errors"] 42 | ): 43 | __all__.append(name) 44 | -------------------------------------------------------------------------------- /src/onepassword/build_number.py: -------------------------------------------------------------------------------- 1 | SDK_BUILD_NUMBER = "0030001" 2 | -------------------------------------------------------------------------------- /src/onepassword/client.py: -------------------------------------------------------------------------------- 1 | # Code generated by op-codegen - DO NO EDIT MANUALLY 2 | 3 | from __future__ import annotations 4 | import weakref 5 | from .core import _init_client, _release_client 6 | from .defaults import new_default_config 7 | from .secrets import Secrets 8 | from .items import Items 9 | from .vaults import Vaults 10 | 11 | 12 | class Client: 13 | secrets: Secrets 14 | items: Items 15 | vaults: Vaults 16 | 17 | @classmethod 18 | async def authenticate( 19 | cls, auth: str, integration_name: str, integration_version: str 20 | ) -> Client: 21 | config = new_default_config( 22 | auth=auth or "", 23 | integration_name=integration_name, 24 | integration_version=integration_version, 25 | ) 26 | 27 | client_id = int(await _init_client(config)) 28 | 29 | authenticated_client = cls() 30 | 31 | authenticated_client.secrets = Secrets(client_id) 32 | authenticated_client.items = Items(client_id) 33 | authenticated_client.vaults = Vaults(client_id) 34 | authenticated_client._finalizer = weakref.finalize( 35 | cls, _release_client, client_id 36 | ) 37 | 38 | return authenticated_client 39 | -------------------------------------------------------------------------------- /src/onepassword/core.py: -------------------------------------------------------------------------------- 1 | import json 2 | import platform 3 | 4 | from onepassword.errors import raise_typed_exception 5 | 6 | # In empirical tests, we determined that maximum message size that can cross the FFI boundary 7 | # is ~128MB. Past this limit, FFI will throw an error and the program will crash. 8 | # We set the limit to 50MB to be safe and consistent with the other SDKs (where this limit is 64MB), to be reconsidered upon further testing 9 | MESSAGE_LIMIT = 50 * 1024 * 1024 10 | 11 | machine_arch = platform.machine().lower() 12 | 13 | if machine_arch in ["x86_64", "amd64"]: 14 | import onepassword.lib.x86_64.op_uniffi_core as core 15 | elif machine_arch in ["aarch64", "arm64"]: 16 | import onepassword.lib.aarch64.op_uniffi_core as core 17 | else: 18 | raise ImportError( 19 | f"Your machine's architecture is not currently supported: {machine_arch}" 20 | ) 21 | 22 | 23 | # InitClient creates a client instance in the current core module and returns its unique ID. 24 | async def _init_client(client_config): 25 | try: 26 | return await core.init_client(json.dumps(client_config)) 27 | except Exception as e: 28 | raise_typed_exception(e) 29 | 30 | 31 | # Invoke calls specified business logic from the SDK core. 32 | async def _invoke(invoke_config): 33 | serialized_config = json.dumps(invoke_config) 34 | if len(serialized_config.encode()) > MESSAGE_LIMIT: 35 | raise ValueError( 36 | f"message size exceeds the limit of {MESSAGE_LIMIT} bytes, please contact 1Password at support@1password.com or https://developer.1password.com/joinslack if you need help." 37 | ) 38 | try: 39 | return await core.invoke(serialized_config) 40 | except Exception as e: 41 | raise_typed_exception(e) 42 | 43 | 44 | # Invoke calls specified business logic from the SDK core. 45 | def _invoke_sync(invoke_config): 46 | serialized_config = json.dumps(invoke_config) 47 | if len(serialized_config.encode()) > MESSAGE_LIMIT: 48 | raise ValueError( 49 | f"message size exceeds the limit of {MESSAGE_LIMIT} bytes, please contact 1Password at support@1password.com or https://developer.1password.com/joinslack if you need help." 50 | ) 51 | try: 52 | return core.invoke_sync(serialized_config) 53 | except Exception as e: 54 | raise_typed_exception(e) 55 | 56 | 57 | # ReleaseClient releases memory in the SDK core associated with the given client ID. 58 | def _release_client(client_id): 59 | return core.release_client(json.dumps(client_id)) 60 | -------------------------------------------------------------------------------- /src/onepassword/defaults.py: -------------------------------------------------------------------------------- 1 | import platform 2 | from onepassword.build_number import SDK_BUILD_NUMBER 3 | 4 | SDK_LANGUAGE = "Python" 5 | SDK_VERSION = SDK_BUILD_NUMBER 6 | DEFAULT_INTEGRATION_NAME = "Unknown" 7 | DEFAULT_INTEGRATION_VERSION = "Unknown" 8 | DEFAULT_REQUEST_LIBRARY = "reqwest" 9 | DEFAULT_REQUEST_LIBRARY_VERSION = "0.11.24" 10 | DEFAULT_OS_VERSION = "0.0.0" 11 | 12 | 13 | # Generates a configuration dictionary with the user's parameters 14 | def new_default_config(auth, integration_name, integration_version): 15 | client_config_dict = { 16 | "serviceAccountToken": auth, 17 | "programmingLanguage": SDK_LANGUAGE, 18 | "sdkVersion": SDK_VERSION, 19 | "integrationName": integration_name, 20 | "integrationVersion": integration_version, 21 | "requestLibraryName": DEFAULT_REQUEST_LIBRARY, 22 | "requestLibraryVersion": DEFAULT_REQUEST_LIBRARY_VERSION, 23 | "os": platform.system().lower(), 24 | "osVersion": DEFAULT_OS_VERSION, 25 | "architecture": platform.machine(), 26 | } 27 | return client_config_dict 28 | -------------------------------------------------------------------------------- /src/onepassword/errors.py: -------------------------------------------------------------------------------- 1 | # Code generated by op-codegen - DO NO EDIT MANUALLY 2 | 3 | import json 4 | 5 | 6 | class RateLimitExceededException(Exception): 7 | def __init__(self, message): 8 | self.message = message 9 | super().__init__(self.message) 10 | 11 | 12 | def raise_typed_exception(e: Exception): 13 | try: 14 | typed_error = json.loads(e.msg) 15 | except Exception: 16 | raise e 17 | 18 | error_name = typed_error.get("name") 19 | message = typed_error.get("message") 20 | 21 | if error_name == "RateLimitExceeded": 22 | raise RateLimitExceededException(message) 23 | elif message is not None: 24 | raise Exception(message) 25 | else: 26 | raise e 27 | -------------------------------------------------------------------------------- /src/onepassword/items.py: -------------------------------------------------------------------------------- 1 | # Code generated by op-codegen - DO NO EDIT MANUALLY 2 | 3 | from .core import _invoke, _invoke_sync 4 | from typing import Optional, List 5 | from pydantic import TypeAdapter 6 | from .items_shares import ItemsShares 7 | from .items_files import ItemsFiles 8 | from .types import Item, ItemCreateParams, ItemListFilter, ItemOverview 9 | 10 | 11 | class Items: 12 | """ 13 | The Items API holds all operations the SDK client can perform on 1Password items. 14 | """ 15 | 16 | def __init__(self, client_id): 17 | self.client_id = client_id 18 | self.shares = ItemsShares(client_id) 19 | 20 | self.files = ItemsFiles(client_id) 21 | 22 | async def create(self, params: ItemCreateParams) -> Item: 23 | """ 24 | Create a new item. 25 | """ 26 | response = await _invoke( 27 | { 28 | "invocation": { 29 | "clientId": self.client_id, 30 | "parameters": { 31 | "name": "ItemsCreate", 32 | "parameters": {"params": params.model_dump(by_alias=True)}, 33 | }, 34 | } 35 | } 36 | ) 37 | 38 | response = TypeAdapter(Item).validate_json(response) 39 | return response 40 | 41 | async def get(self, vault_id: str, item_id: str) -> Item: 42 | """ 43 | Get an item by vault and item ID 44 | """ 45 | response = await _invoke( 46 | { 47 | "invocation": { 48 | "clientId": self.client_id, 49 | "parameters": { 50 | "name": "ItemsGet", 51 | "parameters": {"vault_id": vault_id, "item_id": item_id}, 52 | }, 53 | } 54 | } 55 | ) 56 | 57 | response = TypeAdapter(Item).validate_json(response) 58 | return response 59 | 60 | async def put(self, item: Item) -> Item: 61 | """ 62 | Update an existing item. 63 | """ 64 | response = await _invoke( 65 | { 66 | "invocation": { 67 | "clientId": self.client_id, 68 | "parameters": { 69 | "name": "ItemsPut", 70 | "parameters": {"item": item.model_dump(by_alias=True)}, 71 | }, 72 | } 73 | } 74 | ) 75 | 76 | response = TypeAdapter(Item).validate_json(response) 77 | return response 78 | 79 | async def delete(self, vault_id: str, item_id: str) -> None: 80 | """ 81 | Delete an item. 82 | """ 83 | response = await _invoke( 84 | { 85 | "invocation": { 86 | "clientId": self.client_id, 87 | "parameters": { 88 | "name": "ItemsDelete", 89 | "parameters": {"vault_id": vault_id, "item_id": item_id}, 90 | }, 91 | } 92 | } 93 | ) 94 | 95 | return None 96 | 97 | async def archive(self, vault_id: str, item_id: str) -> None: 98 | """ 99 | Archive an item. 100 | """ 101 | response = await _invoke( 102 | { 103 | "invocation": { 104 | "clientId": self.client_id, 105 | "parameters": { 106 | "name": "ItemsArchive", 107 | "parameters": {"vault_id": vault_id, "item_id": item_id}, 108 | }, 109 | } 110 | } 111 | ) 112 | 113 | return None 114 | 115 | async def list(self, vault_id: str, *filters: ItemListFilter) -> List[ItemOverview]: 116 | """ 117 | List items based on filters. 118 | """ 119 | response = await _invoke( 120 | { 121 | "invocation": { 122 | "clientId": self.client_id, 123 | "parameters": { 124 | "name": "ItemsList", 125 | "parameters": { 126 | "vault_id": vault_id, 127 | "filters": [o.model_dump(by_alias=True) for o in filters], 128 | }, 129 | }, 130 | } 131 | } 132 | ) 133 | 134 | response = TypeAdapter(List[ItemOverview]).validate_json(response) 135 | return response 136 | -------------------------------------------------------------------------------- /src/onepassword/items_files.py: -------------------------------------------------------------------------------- 1 | # Code generated by op-codegen - DO NO EDIT MANUALLY 2 | 3 | from .core import _invoke, _invoke_sync 4 | from typing import Optional, List 5 | from pydantic import TypeAdapter 6 | from .types import DocumentCreateParams, FileAttributes, FileCreateParams, Item 7 | 8 | 9 | class ItemsFiles: 10 | def __init__(self, client_id): 11 | self.client_id = client_id 12 | 13 | async def attach(self, item: Item, file_params: FileCreateParams) -> Item: 14 | """ 15 | Attach files to Items 16 | """ 17 | response = await _invoke( 18 | { 19 | "invocation": { 20 | "clientId": self.client_id, 21 | "parameters": { 22 | "name": "ItemsFilesAttach", 23 | "parameters": { 24 | "item": item.model_dump(by_alias=True), 25 | "file_params": file_params.model_dump(by_alias=True), 26 | }, 27 | }, 28 | } 29 | } 30 | ) 31 | 32 | response = TypeAdapter(Item).validate_json(response) 33 | return response 34 | 35 | async def read(self, vault_id: str, item_id: str, attr: FileAttributes) -> bytes: 36 | """ 37 | Read file content from the Item 38 | """ 39 | response = await _invoke( 40 | { 41 | "invocation": { 42 | "clientId": self.client_id, 43 | "parameters": { 44 | "name": "ItemsFilesRead", 45 | "parameters": { 46 | "vault_id": vault_id, 47 | "item_id": item_id, 48 | "attr": attr.model_dump(by_alias=True), 49 | }, 50 | }, 51 | } 52 | } 53 | ) 54 | 55 | response = bytes(TypeAdapter(List[int]).validate_json(response)) 56 | return response 57 | 58 | async def delete(self, item: Item, section_id: str, field_id: str) -> Item: 59 | """ 60 | Delete a field file from Item using the section and field IDs 61 | """ 62 | response = await _invoke( 63 | { 64 | "invocation": { 65 | "clientId": self.client_id, 66 | "parameters": { 67 | "name": "ItemsFilesDelete", 68 | "parameters": { 69 | "item": item.model_dump(by_alias=True), 70 | "section_id": section_id, 71 | "field_id": field_id, 72 | }, 73 | }, 74 | } 75 | } 76 | ) 77 | 78 | response = TypeAdapter(Item).validate_json(response) 79 | return response 80 | 81 | async def replace_document( 82 | self, item: Item, doc_params: DocumentCreateParams 83 | ) -> Item: 84 | """ 85 | Replace the document file within a document item 86 | """ 87 | response = await _invoke( 88 | { 89 | "invocation": { 90 | "clientId": self.client_id, 91 | "parameters": { 92 | "name": "ItemsFilesReplaceDocument", 93 | "parameters": { 94 | "item": item.model_dump(by_alias=True), 95 | "doc_params": doc_params.model_dump(by_alias=True), 96 | }, 97 | }, 98 | } 99 | } 100 | ) 101 | 102 | response = TypeAdapter(Item).validate_json(response) 103 | return response 104 | -------------------------------------------------------------------------------- /src/onepassword/items_shares.py: -------------------------------------------------------------------------------- 1 | # Code generated by op-codegen - DO NO EDIT MANUALLY 2 | 3 | from .core import _invoke, _invoke_sync 4 | from typing import Optional, List 5 | from pydantic import TypeAdapter 6 | from .types import Item, ItemShareAccountPolicy, ItemShareParams, ValidRecipient 7 | 8 | 9 | class ItemsShares: 10 | def __init__(self, client_id): 11 | self.client_id = client_id 12 | 13 | async def get_account_policy( 14 | self, vault_id: str, item_id: str 15 | ) -> ItemShareAccountPolicy: 16 | """ 17 | Get the item sharing policy of your account. 18 | """ 19 | response = await _invoke( 20 | { 21 | "invocation": { 22 | "clientId": self.client_id, 23 | "parameters": { 24 | "name": "ItemsSharesGetAccountPolicy", 25 | "parameters": {"vault_id": vault_id, "item_id": item_id}, 26 | }, 27 | } 28 | } 29 | ) 30 | 31 | response = TypeAdapter(ItemShareAccountPolicy).validate_json(response) 32 | return response 33 | 34 | async def validate_recipients( 35 | self, policy: ItemShareAccountPolicy, recipients: List[str] 36 | ) -> List[ValidRecipient]: 37 | """ 38 | Validate the recipients of an item sharing link. 39 | """ 40 | response = await _invoke( 41 | { 42 | "invocation": { 43 | "clientId": self.client_id, 44 | "parameters": { 45 | "name": "ItemsSharesValidateRecipients", 46 | "parameters": { 47 | "policy": policy.model_dump(by_alias=True), 48 | "recipients": recipients, 49 | }, 50 | }, 51 | } 52 | } 53 | ) 54 | 55 | response = TypeAdapter(List[ValidRecipient]).validate_json(response) 56 | return response 57 | 58 | async def create( 59 | self, item: Item, policy: ItemShareAccountPolicy, params: ItemShareParams 60 | ) -> str: 61 | """ 62 | Create a new item sharing link. 63 | """ 64 | response = await _invoke( 65 | { 66 | "invocation": { 67 | "clientId": self.client_id, 68 | "parameters": { 69 | "name": "ItemsSharesCreate", 70 | "parameters": { 71 | "item": item.model_dump(by_alias=True), 72 | "policy": policy.model_dump(by_alias=True), 73 | "params": params.model_dump(by_alias=True), 74 | }, 75 | }, 76 | } 77 | } 78 | ) 79 | 80 | response = TypeAdapter(str).validate_json(response) 81 | return response 82 | -------------------------------------------------------------------------------- /src/onepassword/lib/aarch64/libop_uniffi_core.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1Password/onepassword-sdk-python/6885d22c8dac7f34c2c39f96b8819c491db76080/src/onepassword/lib/aarch64/libop_uniffi_core.dylib -------------------------------------------------------------------------------- /src/onepassword/lib/aarch64/libop_uniffi_core.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1Password/onepassword-sdk-python/6885d22c8dac7f34c2c39f96b8819c491db76080/src/onepassword/lib/aarch64/libop_uniffi_core.so -------------------------------------------------------------------------------- /src/onepassword/lib/aarch64/op_uniffi_core.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | # This file was autogenerated by some hot garbage in the `uniffi` crate. 4 | # Trust me, you don't want to mess with it! 5 | 6 | # Common helper code. 7 | # 8 | # Ideally this would live in a separate .py file where it can be unittested etc 9 | # in isolation, and perhaps even published as a re-useable package. 10 | # 11 | # However, it's important that the details of how this helper code works (e.g. the 12 | # way that different builtin types are passed across the FFI) exactly match what's 13 | # expected by the rust code on the other side of the interface. In practice right 14 | # now that means coming from the exact some version of `uniffi` that was used to 15 | # compile the rust component. The easiest way to ensure this is to bundle the Python 16 | # helpers directly inline like we're doing here. 17 | 18 | import os 19 | import sys 20 | import ctypes 21 | import enum 22 | import struct 23 | import contextlib 24 | import datetime 25 | import typing 26 | import asyncio 27 | import platform 28 | 29 | # Used for default argument values 30 | _DEFAULT = object() 31 | 32 | 33 | class _UniffiRustBuffer(ctypes.Structure): 34 | _fields_ = [ 35 | ("capacity", ctypes.c_int32), 36 | ("len", ctypes.c_int32), 37 | ("data", ctypes.POINTER(ctypes.c_char)), 38 | ] 39 | 40 | @staticmethod 41 | def alloc(size): 42 | return _rust_call(_UniffiLib.ffi_op_uniffi_core_rustbuffer_alloc, size) 43 | 44 | @staticmethod 45 | def reserve(rbuf, additional): 46 | return _rust_call(_UniffiLib.ffi_op_uniffi_core_rustbuffer_reserve, rbuf, additional) 47 | 48 | def free(self): 49 | return _rust_call(_UniffiLib.ffi_op_uniffi_core_rustbuffer_free, self) 50 | 51 | def __str__(self): 52 | return "_UniffiRustBuffer(capacity={}, len={}, data={})".format( 53 | self.capacity, 54 | self.len, 55 | self.data[0:self.len] 56 | ) 57 | 58 | @contextlib.contextmanager 59 | def alloc_with_builder(*args): 60 | """Context-manger to allocate a buffer using a _UniffiRustBufferBuilder. 61 | 62 | The allocated buffer will be automatically freed if an error occurs, ensuring that 63 | we don't accidentally leak it. 64 | """ 65 | builder = _UniffiRustBufferBuilder() 66 | try: 67 | yield builder 68 | except: 69 | builder.discard() 70 | raise 71 | 72 | @contextlib.contextmanager 73 | def consume_with_stream(self): 74 | """Context-manager to consume a buffer using a _UniffiRustBufferStream. 75 | 76 | The _UniffiRustBuffer will be freed once the context-manager exits, ensuring that we don't 77 | leak it even if an error occurs. 78 | """ 79 | try: 80 | s = _UniffiRustBufferStream.from_rust_buffer(self) 81 | yield s 82 | if s.remaining() != 0: 83 | raise RuntimeError("junk data left in buffer at end of consume_with_stream") 84 | finally: 85 | self.free() 86 | 87 | @contextlib.contextmanager 88 | def read_with_stream(self): 89 | """Context-manager to read a buffer using a _UniffiRustBufferStream. 90 | 91 | This is like consume_with_stream, but doesn't free the buffer afterwards. 92 | It should only be used with borrowed `_UniffiRustBuffer` data. 93 | """ 94 | s = _UniffiRustBufferStream.from_rust_buffer(self) 95 | yield s 96 | if s.remaining() != 0: 97 | raise RuntimeError("junk data left in buffer at end of read_with_stream") 98 | 99 | class _UniffiForeignBytes(ctypes.Structure): 100 | _fields_ = [ 101 | ("len", ctypes.c_int32), 102 | ("data", ctypes.POINTER(ctypes.c_char)), 103 | ] 104 | 105 | def __str__(self): 106 | return "_UniffiForeignBytes(len={}, data={})".format(self.len, self.data[0:self.len]) 107 | 108 | 109 | class _UniffiRustBufferStream: 110 | """ 111 | Helper for structured reading of bytes from a _UniffiRustBuffer 112 | """ 113 | 114 | def __init__(self, data, len): 115 | self.data = data 116 | self.len = len 117 | self.offset = 0 118 | 119 | @classmethod 120 | def from_rust_buffer(cls, buf): 121 | return cls(buf.data, buf.len) 122 | 123 | def remaining(self): 124 | return self.len - self.offset 125 | 126 | def _unpack_from(self, size, format): 127 | if self.offset + size > self.len: 128 | raise InternalError("read past end of rust buffer") 129 | value = struct.unpack(format, self.data[self.offset:self.offset+size])[0] 130 | self.offset += size 131 | return value 132 | 133 | def read(self, size): 134 | if self.offset + size > self.len: 135 | raise InternalError("read past end of rust buffer") 136 | data = self.data[self.offset:self.offset+size] 137 | self.offset += size 138 | return data 139 | 140 | def read_i8(self): 141 | return self._unpack_from(1, ">b") 142 | 143 | def read_u8(self): 144 | return self._unpack_from(1, ">B") 145 | 146 | def read_i16(self): 147 | return self._unpack_from(2, ">h") 148 | 149 | def read_u16(self): 150 | return self._unpack_from(2, ">H") 151 | 152 | def read_i32(self): 153 | return self._unpack_from(4, ">i") 154 | 155 | def read_u32(self): 156 | return self._unpack_from(4, ">I") 157 | 158 | def read_i64(self): 159 | return self._unpack_from(8, ">q") 160 | 161 | def read_u64(self): 162 | return self._unpack_from(8, ">Q") 163 | 164 | def read_float(self): 165 | v = self._unpack_from(4, ">f") 166 | return v 167 | 168 | def read_double(self): 169 | return self._unpack_from(8, ">d") 170 | 171 | def read_c_size_t(self): 172 | return self._unpack_from(ctypes.sizeof(ctypes.c_size_t) , "@N") 173 | 174 | class _UniffiRustBufferBuilder: 175 | """ 176 | Helper for structured writing of bytes into a _UniffiRustBuffer. 177 | """ 178 | 179 | def __init__(self): 180 | self.rbuf = _UniffiRustBuffer.alloc(16) 181 | self.rbuf.len = 0 182 | 183 | def finalize(self): 184 | rbuf = self.rbuf 185 | self.rbuf = None 186 | return rbuf 187 | 188 | def discard(self): 189 | if self.rbuf is not None: 190 | rbuf = self.finalize() 191 | rbuf.free() 192 | 193 | @contextlib.contextmanager 194 | def _reserve(self, num_bytes): 195 | if self.rbuf.len + num_bytes > self.rbuf.capacity: 196 | self.rbuf = _UniffiRustBuffer.reserve(self.rbuf, num_bytes) 197 | yield None 198 | self.rbuf.len += num_bytes 199 | 200 | def _pack_into(self, size, format, value): 201 | with self._reserve(size): 202 | # XXX TODO: I feel like I should be able to use `struct.pack_into` here but can't figure it out. 203 | for i, byte in enumerate(struct.pack(format, value)): 204 | self.rbuf.data[self.rbuf.len + i] = byte 205 | 206 | def write(self, value): 207 | with self._reserve(len(value)): 208 | for i, byte in enumerate(value): 209 | self.rbuf.data[self.rbuf.len + i] = byte 210 | 211 | def write_i8(self, v): 212 | self._pack_into(1, ">b", v) 213 | 214 | def write_u8(self, v): 215 | self._pack_into(1, ">B", v) 216 | 217 | def write_i16(self, v): 218 | self._pack_into(2, ">h", v) 219 | 220 | def write_u16(self, v): 221 | self._pack_into(2, ">H", v) 222 | 223 | def write_i32(self, v): 224 | self._pack_into(4, ">i", v) 225 | 226 | def write_u32(self, v): 227 | self._pack_into(4, ">I", v) 228 | 229 | def write_i64(self, v): 230 | self._pack_into(8, ">q", v) 231 | 232 | def write_u64(self, v): 233 | self._pack_into(8, ">Q", v) 234 | 235 | def write_float(self, v): 236 | self._pack_into(4, ">f", v) 237 | 238 | def write_double(self, v): 239 | self._pack_into(8, ">d", v) 240 | 241 | def write_c_size_t(self, v): 242 | self._pack_into(ctypes.sizeof(ctypes.c_size_t) , "@N", v) 243 | # A handful of classes and functions to support the generated data structures. 244 | # This would be a good candidate for isolating in its own ffi-support lib. 245 | 246 | class InternalError(Exception): 247 | pass 248 | 249 | class _UniffiRustCallStatus(ctypes.Structure): 250 | """ 251 | Error runtime. 252 | """ 253 | _fields_ = [ 254 | ("code", ctypes.c_int8), 255 | ("error_buf", _UniffiRustBuffer), 256 | ] 257 | 258 | # These match the values from the uniffi::rustcalls module 259 | CALL_SUCCESS = 0 260 | CALL_ERROR = 1 261 | CALL_PANIC = 2 262 | 263 | def __str__(self): 264 | if self.code == _UniffiRustCallStatus.CALL_SUCCESS: 265 | return "_UniffiRustCallStatus(CALL_SUCCESS)" 266 | elif self.code == _UniffiRustCallStatus.CALL_ERROR: 267 | return "_UniffiRustCallStatus(CALL_ERROR)" 268 | elif self.code == _UniffiRustCallStatus.CALL_PANIC: 269 | return "_UniffiRustCallStatus(CALL_PANIC)" 270 | else: 271 | return "_UniffiRustCallStatus()" 272 | 273 | def _rust_call(fn, *args): 274 | # Call a rust function 275 | return _rust_call_with_error(None, fn, *args) 276 | 277 | def _rust_call_with_error(error_ffi_converter, fn, *args): 278 | # Call a rust function and handle any errors 279 | # 280 | # This function is used for rust calls that return Result<> and therefore can set the CALL_ERROR status code. 281 | # error_ffi_converter must be set to the _UniffiConverter for the error class that corresponds to the result. 282 | call_status = _UniffiRustCallStatus(code=_UniffiRustCallStatus.CALL_SUCCESS, error_buf=_UniffiRustBuffer(0, 0, None)) 283 | 284 | args_with_error = args + (ctypes.byref(call_status),) 285 | result = fn(*args_with_error) 286 | _uniffi_check_call_status(error_ffi_converter, call_status) 287 | return result 288 | 289 | def _uniffi_check_call_status(error_ffi_converter, call_status): 290 | if call_status.code == _UniffiRustCallStatus.CALL_SUCCESS: 291 | pass 292 | elif call_status.code == _UniffiRustCallStatus.CALL_ERROR: 293 | if error_ffi_converter is None: 294 | call_status.error_buf.free() 295 | raise InternalError("_rust_call_with_error: CALL_ERROR, but error_ffi_converter is None") 296 | else: 297 | raise error_ffi_converter.lift(call_status.error_buf) 298 | elif call_status.code == _UniffiRustCallStatus.CALL_PANIC: 299 | # When the rust code sees a panic, it tries to construct a _UniffiRustBuffer 300 | # with the message. But if that code panics, then it just sends back 301 | # an empty buffer. 302 | if call_status.error_buf.len > 0: 303 | msg = _UniffiConverterString.lift(call_status.error_buf) 304 | else: 305 | msg = "Unknown rust panic" 306 | raise InternalError(msg) 307 | else: 308 | raise InternalError("Invalid _UniffiRustCallStatus code: {}".format( 309 | call_status.code)) 310 | 311 | # A function pointer for a callback as defined by UniFFI. 312 | # Rust definition `fn(handle: u64, method: u32, args: _UniffiRustBuffer, buf_ptr: *mut _UniffiRustBuffer) -> int` 313 | _UNIFFI_FOREIGN_CALLBACK_T = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_ulonglong, ctypes.c_ulong, ctypes.POINTER(ctypes.c_char), ctypes.c_int, ctypes.POINTER(_UniffiRustBuffer)) 314 | 315 | # UniFFI future continuation 316 | _UNIFFI_FUTURE_CONTINUATION_T = ctypes.CFUNCTYPE(None, ctypes.c_size_t, ctypes.c_int8) 317 | 318 | class _UniffiPointerManagerCPython: 319 | """ 320 | Manage giving out pointers to Python objects on CPython 321 | 322 | This class is used to generate opaque pointers that reference Python objects to pass to Rust. 323 | It assumes a CPython platform. See _UniffiPointerManagerGeneral for the alternative. 324 | """ 325 | 326 | def new_pointer(self, obj): 327 | """ 328 | Get a pointer for an object as a ctypes.c_size_t instance 329 | 330 | Each call to new_pointer() must be balanced with exactly one call to release_pointer() 331 | 332 | This returns a ctypes.c_size_t. This is always the same size as a pointer and can be 333 | interchanged with pointers for FFI function arguments and return values. 334 | """ 335 | # IncRef the object since we're going to pass a pointer to Rust 336 | ctypes.pythonapi.Py_IncRef(ctypes.py_object(obj)) 337 | # id() is the object address on CPython 338 | # (https://docs.python.org/3/library/functions.html#id) 339 | return id(obj) 340 | 341 | def release_pointer(self, address): 342 | py_obj = ctypes.cast(address, ctypes.py_object) 343 | obj = py_obj.value 344 | ctypes.pythonapi.Py_DecRef(py_obj) 345 | return obj 346 | 347 | def lookup(self, address): 348 | return ctypes.cast(address, ctypes.py_object).value 349 | 350 | class _UniffiPointerManagerGeneral: 351 | """ 352 | Manage giving out pointers to Python objects on non-CPython platforms 353 | 354 | This has the same API as _UniffiPointerManagerCPython, but doesn't assume we're running on 355 | CPython and is slightly slower. 356 | 357 | Instead of using real pointers, it maps integer values to objects and returns the keys as 358 | c_size_t values. 359 | """ 360 | 361 | def __init__(self): 362 | self._map = {} 363 | self._lock = threading.Lock() 364 | self._current_handle = 0 365 | 366 | def new_pointer(self, obj): 367 | with self._lock: 368 | handle = self._current_handle 369 | self._current_handle += 1 370 | self._map[handle] = obj 371 | return handle 372 | 373 | def release_pointer(self, handle): 374 | with self._lock: 375 | return self._map.pop(handle) 376 | 377 | def lookup(self, handle): 378 | with self._lock: 379 | return self._map[handle] 380 | 381 | # Pick an pointer manager implementation based on the platform 382 | if platform.python_implementation() == 'CPython': 383 | _UniffiPointerManager = _UniffiPointerManagerCPython # type: ignore 384 | else: 385 | _UniffiPointerManager = _UniffiPointerManagerGeneral # type: ignore 386 | # Types conforming to `_UniffiConverterPrimitive` pass themselves directly over the FFI. 387 | class _UniffiConverterPrimitive: 388 | @classmethod 389 | def lift(cls, value): 390 | return value 391 | 392 | @classmethod 393 | def lower(cls, value): 394 | return value 395 | 396 | class _UniffiConverterPrimitiveInt(_UniffiConverterPrimitive): 397 | @classmethod 398 | def check_lower(cls, value): 399 | try: 400 | value = value.__index__() 401 | except Exception: 402 | raise TypeError("'{}' object cannot be interpreted as an integer".format(type(value).__name__)) 403 | if not isinstance(value, int): 404 | raise TypeError("__index__ returned non-int (type {})".format(type(value).__name__)) 405 | if not cls.VALUE_MIN <= value < cls.VALUE_MAX: 406 | raise ValueError("{} requires {} <= value < {}".format(cls.CLASS_NAME, cls.VALUE_MIN, cls.VALUE_MAX)) 407 | 408 | class _UniffiConverterPrimitiveFloat(_UniffiConverterPrimitive): 409 | @classmethod 410 | def check_lower(cls, value): 411 | try: 412 | value = value.__float__() 413 | except Exception: 414 | raise TypeError("must be real number, not {}".format(type(value).__name__)) 415 | if not isinstance(value, float): 416 | raise TypeError("__float__ returned non-float (type {})".format(type(value).__name__)) 417 | 418 | # Helper class for wrapper types that will always go through a _UniffiRustBuffer. 419 | # Classes should inherit from this and implement the `read` and `write` static methods. 420 | class _UniffiConverterRustBuffer: 421 | @classmethod 422 | def lift(cls, rbuf): 423 | with rbuf.consume_with_stream() as stream: 424 | return cls.read(stream) 425 | 426 | @classmethod 427 | def lower(cls, value): 428 | with _UniffiRustBuffer.alloc_with_builder() as builder: 429 | cls.write(value, builder) 430 | return builder.finalize() 431 | 432 | # Contains loading, initialization code, and the FFI Function declarations. 433 | # Define some ctypes FFI types that we use in the library 434 | 435 | """ 436 | Function pointer for a Rust task, which a callback function that takes a opaque pointer 437 | """ 438 | _UNIFFI_RUST_TASK = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_int8) 439 | 440 | def _uniffi_future_callback_t(return_type): 441 | """ 442 | Factory function to create callback function types for async functions 443 | """ 444 | return ctypes.CFUNCTYPE(None, ctypes.c_size_t, return_type, _UniffiRustCallStatus) 445 | 446 | def _uniffi_load_indirect(): 447 | """ 448 | This is how we find and load the dynamic library provided by the component. 449 | For now we just look it up by name. 450 | """ 451 | if sys.platform == "darwin": 452 | libname = "lib{}.dylib" 453 | elif sys.platform.startswith("win"): 454 | # As of python3.8, ctypes does not seem to search $PATH when loading DLLs. 455 | # We could use `os.add_dll_directory` to configure the search path, but 456 | # it doesn't feel right to mess with application-wide settings. Let's 457 | # assume that the `.dll` is next to the `.py` file and load by full path. 458 | libname = os.path.join( 459 | os.path.dirname(__file__), 460 | "{}.dll", 461 | ) 462 | else: 463 | # Anything else must be an ELF platform - Linux, *BSD, Solaris/illumos 464 | libname = "lib{}.so" 465 | 466 | libname = libname.format("op_uniffi_core") 467 | path = os.path.join(os.path.dirname(__file__), libname) 468 | lib = ctypes.cdll.LoadLibrary(path) 469 | return lib 470 | 471 | def _uniffi_check_contract_api_version(lib): 472 | # Get the bindings contract version from our ComponentInterface 473 | bindings_contract_version = 25 474 | # Get the scaffolding contract version by calling the into the dylib 475 | scaffolding_contract_version = lib.ffi_op_uniffi_core_uniffi_contract_version() 476 | if bindings_contract_version != scaffolding_contract_version: 477 | raise InternalError("UniFFI contract version mismatch: try cleaning and rebuilding your project") 478 | 479 | def _uniffi_check_api_checksums(lib): 480 | if lib.uniffi_op_uniffi_core_checksum_func_init_client() != 45066: 481 | raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") 482 | if lib.uniffi_op_uniffi_core_checksum_func_invoke() != 29143: 483 | raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") 484 | if lib.uniffi_op_uniffi_core_checksum_func_invoke_sync() != 49373: 485 | raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") 486 | if lib.uniffi_op_uniffi_core_checksum_func_release_client() != 57155: 487 | raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") 488 | 489 | # A ctypes library to expose the extern-C FFI definitions. 490 | # This is an implementation detail which will be called internally by the public API. 491 | 492 | _UniffiLib = _uniffi_load_indirect() 493 | _UniffiLib.uniffi_op_uniffi_core_fn_func_init_client.argtypes = ( 494 | _UniffiRustBuffer, 495 | ) 496 | _UniffiLib.uniffi_op_uniffi_core_fn_func_init_client.restype = ctypes.c_void_p 497 | _UniffiLib.uniffi_op_uniffi_core_fn_func_invoke.argtypes = ( 498 | _UniffiRustBuffer, 499 | ) 500 | _UniffiLib.uniffi_op_uniffi_core_fn_func_invoke.restype = ctypes.c_void_p 501 | _UniffiLib.uniffi_op_uniffi_core_fn_func_invoke_sync.argtypes = ( 502 | _UniffiRustBuffer, 503 | ctypes.POINTER(_UniffiRustCallStatus), 504 | ) 505 | _UniffiLib.uniffi_op_uniffi_core_fn_func_invoke_sync.restype = _UniffiRustBuffer 506 | _UniffiLib.uniffi_op_uniffi_core_fn_func_release_client.argtypes = ( 507 | _UniffiRustBuffer, 508 | ctypes.POINTER(_UniffiRustCallStatus), 509 | ) 510 | _UniffiLib.uniffi_op_uniffi_core_fn_func_release_client.restype = None 511 | _UniffiLib.ffi_op_uniffi_core_rustbuffer_alloc.argtypes = ( 512 | ctypes.c_int32, 513 | ctypes.POINTER(_UniffiRustCallStatus), 514 | ) 515 | _UniffiLib.ffi_op_uniffi_core_rustbuffer_alloc.restype = _UniffiRustBuffer 516 | _UniffiLib.ffi_op_uniffi_core_rustbuffer_from_bytes.argtypes = ( 517 | _UniffiForeignBytes, 518 | ctypes.POINTER(_UniffiRustCallStatus), 519 | ) 520 | _UniffiLib.ffi_op_uniffi_core_rustbuffer_from_bytes.restype = _UniffiRustBuffer 521 | _UniffiLib.ffi_op_uniffi_core_rustbuffer_free.argtypes = ( 522 | _UniffiRustBuffer, 523 | ctypes.POINTER(_UniffiRustCallStatus), 524 | ) 525 | _UniffiLib.ffi_op_uniffi_core_rustbuffer_free.restype = None 526 | _UniffiLib.ffi_op_uniffi_core_rustbuffer_reserve.argtypes = ( 527 | _UniffiRustBuffer, 528 | ctypes.c_int32, 529 | ctypes.POINTER(_UniffiRustCallStatus), 530 | ) 531 | _UniffiLib.ffi_op_uniffi_core_rustbuffer_reserve.restype = _UniffiRustBuffer 532 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_u8.argtypes = ( 533 | ctypes.c_void_p, 534 | _UNIFFI_FUTURE_CONTINUATION_T, 535 | ctypes.c_size_t, 536 | ) 537 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_u8.restype = None 538 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u8.argtypes = ( 539 | ctypes.c_void_p, 540 | ) 541 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u8.restype = None 542 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_u8.argtypes = ( 543 | ctypes.c_void_p, 544 | ) 545 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_u8.restype = None 546 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_u8.argtypes = ( 547 | ctypes.c_void_p, 548 | ctypes.POINTER(_UniffiRustCallStatus), 549 | ) 550 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_u8.restype = ctypes.c_uint8 551 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_i8.argtypes = ( 552 | ctypes.c_void_p, 553 | _UNIFFI_FUTURE_CONTINUATION_T, 554 | ctypes.c_size_t, 555 | ) 556 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_i8.restype = None 557 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i8.argtypes = ( 558 | ctypes.c_void_p, 559 | ) 560 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i8.restype = None 561 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_i8.argtypes = ( 562 | ctypes.c_void_p, 563 | ) 564 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_i8.restype = None 565 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_i8.argtypes = ( 566 | ctypes.c_void_p, 567 | ctypes.POINTER(_UniffiRustCallStatus), 568 | ) 569 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_i8.restype = ctypes.c_int8 570 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_u16.argtypes = ( 571 | ctypes.c_void_p, 572 | _UNIFFI_FUTURE_CONTINUATION_T, 573 | ctypes.c_size_t, 574 | ) 575 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_u16.restype = None 576 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u16.argtypes = ( 577 | ctypes.c_void_p, 578 | ) 579 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u16.restype = None 580 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_u16.argtypes = ( 581 | ctypes.c_void_p, 582 | ) 583 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_u16.restype = None 584 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_u16.argtypes = ( 585 | ctypes.c_void_p, 586 | ctypes.POINTER(_UniffiRustCallStatus), 587 | ) 588 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_u16.restype = ctypes.c_uint16 589 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_i16.argtypes = ( 590 | ctypes.c_void_p, 591 | _UNIFFI_FUTURE_CONTINUATION_T, 592 | ctypes.c_size_t, 593 | ) 594 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_i16.restype = None 595 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i16.argtypes = ( 596 | ctypes.c_void_p, 597 | ) 598 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i16.restype = None 599 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_i16.argtypes = ( 600 | ctypes.c_void_p, 601 | ) 602 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_i16.restype = None 603 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_i16.argtypes = ( 604 | ctypes.c_void_p, 605 | ctypes.POINTER(_UniffiRustCallStatus), 606 | ) 607 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_i16.restype = ctypes.c_int16 608 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_u32.argtypes = ( 609 | ctypes.c_void_p, 610 | _UNIFFI_FUTURE_CONTINUATION_T, 611 | ctypes.c_size_t, 612 | ) 613 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_u32.restype = None 614 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u32.argtypes = ( 615 | ctypes.c_void_p, 616 | ) 617 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u32.restype = None 618 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_u32.argtypes = ( 619 | ctypes.c_void_p, 620 | ) 621 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_u32.restype = None 622 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_u32.argtypes = ( 623 | ctypes.c_void_p, 624 | ctypes.POINTER(_UniffiRustCallStatus), 625 | ) 626 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_u32.restype = ctypes.c_uint32 627 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_i32.argtypes = ( 628 | ctypes.c_void_p, 629 | _UNIFFI_FUTURE_CONTINUATION_T, 630 | ctypes.c_size_t, 631 | ) 632 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_i32.restype = None 633 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i32.argtypes = ( 634 | ctypes.c_void_p, 635 | ) 636 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i32.restype = None 637 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_i32.argtypes = ( 638 | ctypes.c_void_p, 639 | ) 640 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_i32.restype = None 641 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_i32.argtypes = ( 642 | ctypes.c_void_p, 643 | ctypes.POINTER(_UniffiRustCallStatus), 644 | ) 645 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_i32.restype = ctypes.c_int32 646 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_u64.argtypes = ( 647 | ctypes.c_void_p, 648 | _UNIFFI_FUTURE_CONTINUATION_T, 649 | ctypes.c_size_t, 650 | ) 651 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_u64.restype = None 652 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u64.argtypes = ( 653 | ctypes.c_void_p, 654 | ) 655 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u64.restype = None 656 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_u64.argtypes = ( 657 | ctypes.c_void_p, 658 | ) 659 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_u64.restype = None 660 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_u64.argtypes = ( 661 | ctypes.c_void_p, 662 | ctypes.POINTER(_UniffiRustCallStatus), 663 | ) 664 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_u64.restype = ctypes.c_uint64 665 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_i64.argtypes = ( 666 | ctypes.c_void_p, 667 | _UNIFFI_FUTURE_CONTINUATION_T, 668 | ctypes.c_size_t, 669 | ) 670 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_i64.restype = None 671 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i64.argtypes = ( 672 | ctypes.c_void_p, 673 | ) 674 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i64.restype = None 675 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_i64.argtypes = ( 676 | ctypes.c_void_p, 677 | ) 678 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_i64.restype = None 679 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_i64.argtypes = ( 680 | ctypes.c_void_p, 681 | ctypes.POINTER(_UniffiRustCallStatus), 682 | ) 683 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_i64.restype = ctypes.c_int64 684 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_f32.argtypes = ( 685 | ctypes.c_void_p, 686 | _UNIFFI_FUTURE_CONTINUATION_T, 687 | ctypes.c_size_t, 688 | ) 689 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_f32.restype = None 690 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_f32.argtypes = ( 691 | ctypes.c_void_p, 692 | ) 693 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_f32.restype = None 694 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_f32.argtypes = ( 695 | ctypes.c_void_p, 696 | ) 697 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_f32.restype = None 698 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_f32.argtypes = ( 699 | ctypes.c_void_p, 700 | ctypes.POINTER(_UniffiRustCallStatus), 701 | ) 702 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_f32.restype = ctypes.c_float 703 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_f64.argtypes = ( 704 | ctypes.c_void_p, 705 | _UNIFFI_FUTURE_CONTINUATION_T, 706 | ctypes.c_size_t, 707 | ) 708 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_f64.restype = None 709 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_f64.argtypes = ( 710 | ctypes.c_void_p, 711 | ) 712 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_f64.restype = None 713 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_f64.argtypes = ( 714 | ctypes.c_void_p, 715 | ) 716 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_f64.restype = None 717 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_f64.argtypes = ( 718 | ctypes.c_void_p, 719 | ctypes.POINTER(_UniffiRustCallStatus), 720 | ) 721 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_f64.restype = ctypes.c_double 722 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_pointer.argtypes = ( 723 | ctypes.c_void_p, 724 | _UNIFFI_FUTURE_CONTINUATION_T, 725 | ctypes.c_size_t, 726 | ) 727 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_pointer.restype = None 728 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_pointer.argtypes = ( 729 | ctypes.c_void_p, 730 | ) 731 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_pointer.restype = None 732 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_pointer.argtypes = ( 733 | ctypes.c_void_p, 734 | ) 735 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_pointer.restype = None 736 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_pointer.argtypes = ( 737 | ctypes.c_void_p, 738 | ctypes.POINTER(_UniffiRustCallStatus), 739 | ) 740 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_pointer.restype = ctypes.c_void_p 741 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_rust_buffer.argtypes = ( 742 | ctypes.c_void_p, 743 | _UNIFFI_FUTURE_CONTINUATION_T, 744 | ctypes.c_size_t, 745 | ) 746 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_rust_buffer.restype = None 747 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_rust_buffer.argtypes = ( 748 | ctypes.c_void_p, 749 | ) 750 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_rust_buffer.restype = None 751 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_rust_buffer.argtypes = ( 752 | ctypes.c_void_p, 753 | ) 754 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_rust_buffer.restype = None 755 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_rust_buffer.argtypes = ( 756 | ctypes.c_void_p, 757 | ctypes.POINTER(_UniffiRustCallStatus), 758 | ) 759 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_rust_buffer.restype = _UniffiRustBuffer 760 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_void.argtypes = ( 761 | ctypes.c_void_p, 762 | _UNIFFI_FUTURE_CONTINUATION_T, 763 | ctypes.c_size_t, 764 | ) 765 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_void.restype = None 766 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_void.argtypes = ( 767 | ctypes.c_void_p, 768 | ) 769 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_void.restype = None 770 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_void.argtypes = ( 771 | ctypes.c_void_p, 772 | ) 773 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_void.restype = None 774 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_void.argtypes = ( 775 | ctypes.c_void_p, 776 | ctypes.POINTER(_UniffiRustCallStatus), 777 | ) 778 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_void.restype = None 779 | _UniffiLib.uniffi_op_uniffi_core_checksum_func_init_client.argtypes = ( 780 | ) 781 | _UniffiLib.uniffi_op_uniffi_core_checksum_func_init_client.restype = ctypes.c_uint16 782 | _UniffiLib.uniffi_op_uniffi_core_checksum_func_invoke.argtypes = ( 783 | ) 784 | _UniffiLib.uniffi_op_uniffi_core_checksum_func_invoke.restype = ctypes.c_uint16 785 | _UniffiLib.uniffi_op_uniffi_core_checksum_func_invoke_sync.argtypes = ( 786 | ) 787 | _UniffiLib.uniffi_op_uniffi_core_checksum_func_invoke_sync.restype = ctypes.c_uint16 788 | _UniffiLib.uniffi_op_uniffi_core_checksum_func_release_client.argtypes = ( 789 | ) 790 | _UniffiLib.uniffi_op_uniffi_core_checksum_func_release_client.restype = ctypes.c_uint16 791 | _UniffiLib.ffi_op_uniffi_core_uniffi_contract_version.argtypes = ( 792 | ) 793 | _UniffiLib.ffi_op_uniffi_core_uniffi_contract_version.restype = ctypes.c_uint32 794 | _uniffi_check_contract_api_version(_UniffiLib) 795 | _uniffi_check_api_checksums(_UniffiLib) 796 | 797 | # Async support# RustFuturePoll values 798 | _UNIFFI_RUST_FUTURE_POLL_READY = 0 799 | _UNIFFI_RUST_FUTURE_POLL_MAYBE_READY = 1 800 | 801 | # Stores futures for _uniffi_continuation_callback 802 | _UniffiContinuationPointerManager = _UniffiPointerManager() 803 | 804 | # Continuation callback for async functions 805 | # lift the return value or error and resolve the future, causing the async function to resume. 806 | @_UNIFFI_FUTURE_CONTINUATION_T 807 | def _uniffi_continuation_callback(future_ptr, poll_code): 808 | (eventloop, future) = _UniffiContinuationPointerManager.release_pointer(future_ptr) 809 | eventloop.call_soon_threadsafe(_uniffi_set_future_result, future, poll_code) 810 | 811 | def _uniffi_set_future_result(future, poll_code): 812 | if not future.cancelled(): 813 | future.set_result(poll_code) 814 | 815 | async def _uniffi_rust_call_async(rust_future, ffi_poll, ffi_complete, ffi_free, lift_func, error_ffi_converter): 816 | try: 817 | eventloop = asyncio.get_running_loop() 818 | 819 | # Loop and poll until we see a _UNIFFI_RUST_FUTURE_POLL_READY value 820 | while True: 821 | future = eventloop.create_future() 822 | ffi_poll( 823 | rust_future, 824 | _uniffi_continuation_callback, 825 | _UniffiContinuationPointerManager.new_pointer((eventloop, future)), 826 | ) 827 | poll_code = await future 828 | if poll_code == _UNIFFI_RUST_FUTURE_POLL_READY: 829 | break 830 | 831 | return lift_func( 832 | _rust_call_with_error(error_ffi_converter, ffi_complete, rust_future) 833 | ) 834 | finally: 835 | ffi_free(rust_future) 836 | 837 | # Public interface members begin here. 838 | 839 | 840 | class _UniffiConverterString: 841 | @staticmethod 842 | def check_lower(value): 843 | if not isinstance(value, str): 844 | raise TypeError("argument must be str, not {}".format(type(value).__name__)) 845 | return value 846 | 847 | @staticmethod 848 | def read(buf): 849 | size = buf.read_i32() 850 | if size < 0: 851 | raise InternalError("Unexpected negative string length") 852 | utf8_bytes = buf.read(size) 853 | return utf8_bytes.decode("utf-8") 854 | 855 | @staticmethod 856 | def write(value, buf): 857 | utf8_bytes = value.encode("utf-8") 858 | buf.write_i32(len(utf8_bytes)) 859 | buf.write(utf8_bytes) 860 | 861 | @staticmethod 862 | def lift(buf): 863 | with buf.consume_with_stream() as stream: 864 | return stream.read(stream.remaining()).decode("utf-8") 865 | 866 | @staticmethod 867 | def lower(value): 868 | with _UniffiRustBuffer.alloc_with_builder() as builder: 869 | builder.write(value.encode("utf-8")) 870 | return builder.finalize() 871 | 872 | 873 | # Error 874 | # We want to define each variant as a nested class that's also a subclass, 875 | # which is tricky in Python. To accomplish this we're going to create each 876 | # class separately, then manually add the child classes to the base class's 877 | # __dict__. All of this happens in dummy class to avoid polluting the module 878 | # namespace. 879 | class Error(Exception): 880 | """ 881 | Error type sent over the FFI by UniFFI. 882 | 883 | `uniffi::Error` only supports errors that are enums, so we need to have a single-variant enum here. 884 | """ 885 | 886 | pass 887 | 888 | _UniffiTempError = Error 889 | 890 | class Error: # type: ignore 891 | class Error(_UniffiTempError): 892 | """ 893 | Any error ocurring in the SDK 894 | """ 895 | 896 | 897 | def __init__(self, msg): 898 | super().__init__(", ".join([ 899 | "msg={!r}".format(msg), 900 | ])) 901 | self.msg = msg 902 | def __repr__(self): 903 | return "Error.Error({})".format(str(self)) 904 | _UniffiTempError.Error = Error # type: ignore 905 | 906 | Error = _UniffiTempError # type: ignore 907 | del _UniffiTempError 908 | 909 | 910 | class _UniffiConverterTypeError(_UniffiConverterRustBuffer): 911 | @staticmethod 912 | def read(buf): 913 | variant = buf.read_i32() 914 | if variant == 1: 915 | return Error.Error( 916 | msg=_UniffiConverterString.read(buf), 917 | ) 918 | raise InternalError("Raw enum value doesn't match any cases") 919 | 920 | @staticmethod 921 | def check_lower(value): 922 | if isinstance(value, Error.Error): 923 | _UniffiConverterString.check_lower(value.msg) 924 | return 925 | 926 | @staticmethod 927 | def write(value, buf): 928 | if isinstance(value, Error.Error): 929 | buf.write_i32(1) 930 | _UniffiConverterString.write(value.msg, buf) 931 | 932 | def init_client(client_config: "str"): 933 | _UniffiConverterString.check_lower(client_config) 934 | 935 | return _uniffi_rust_call_async( 936 | _UniffiLib.uniffi_op_uniffi_core_fn_func_init_client( 937 | _UniffiConverterString.lower(client_config)), 938 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_rust_buffer, 939 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_rust_buffer, 940 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_rust_buffer, 941 | # lift function 942 | _UniffiConverterString.lift, 943 | # Error FFI converter 944 | _UniffiConverterTypeError, 945 | ) 946 | 947 | def invoke(invocation: "str"): 948 | _UniffiConverterString.check_lower(invocation) 949 | 950 | return _uniffi_rust_call_async( 951 | _UniffiLib.uniffi_op_uniffi_core_fn_func_invoke( 952 | _UniffiConverterString.lower(invocation)), 953 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_rust_buffer, 954 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_rust_buffer, 955 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_rust_buffer, 956 | # lift function 957 | _UniffiConverterString.lift, 958 | # Error FFI converter 959 | _UniffiConverterTypeError, 960 | ) 961 | 962 | def invoke_sync(invocation: "str") -> "str": 963 | _UniffiConverterString.check_lower(invocation) 964 | 965 | return _UniffiConverterString.lift(_rust_call_with_error(_UniffiConverterTypeError,_UniffiLib.uniffi_op_uniffi_core_fn_func_invoke_sync, 966 | _UniffiConverterString.lower(invocation))) 967 | 968 | 969 | def release_client(client_id: "str"): 970 | _UniffiConverterString.check_lower(client_id) 971 | 972 | _rust_call_with_error(_UniffiConverterTypeError,_UniffiLib.uniffi_op_uniffi_core_fn_func_release_client, 973 | _UniffiConverterString.lower(client_id)) 974 | 975 | 976 | __all__ = [ 977 | "InternalError", 978 | "Error", 979 | "init_client", 980 | "invoke", 981 | "invoke_sync", 982 | "release_client", 983 | ] 984 | 985 | -------------------------------------------------------------------------------- /src/onepassword/lib/x86_64/libop_uniffi_core.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1Password/onepassword-sdk-python/6885d22c8dac7f34c2c39f96b8819c491db76080/src/onepassword/lib/x86_64/libop_uniffi_core.dylib -------------------------------------------------------------------------------- /src/onepassword/lib/x86_64/libop_uniffi_core.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1Password/onepassword-sdk-python/6885d22c8dac7f34c2c39f96b8819c491db76080/src/onepassword/lib/x86_64/libop_uniffi_core.so -------------------------------------------------------------------------------- /src/onepassword/lib/x86_64/op_uniffi_core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1Password/onepassword-sdk-python/6885d22c8dac7f34c2c39f96b8819c491db76080/src/onepassword/lib/x86_64/op_uniffi_core.dll -------------------------------------------------------------------------------- /src/onepassword/lib/x86_64/op_uniffi_core.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | # This file was autogenerated by some hot garbage in the `uniffi` crate. 4 | # Trust me, you don't want to mess with it! 5 | 6 | # Common helper code. 7 | # 8 | # Ideally this would live in a separate .py file where it can be unittested etc 9 | # in isolation, and perhaps even published as a re-useable package. 10 | # 11 | # However, it's important that the details of how this helper code works (e.g. the 12 | # way that different builtin types are passed across the FFI) exactly match what's 13 | # expected by the rust code on the other side of the interface. In practice right 14 | # now that means coming from the exact some version of `uniffi` that was used to 15 | # compile the rust component. The easiest way to ensure this is to bundle the Python 16 | # helpers directly inline like we're doing here. 17 | 18 | import os 19 | import sys 20 | import ctypes 21 | import enum 22 | import struct 23 | import contextlib 24 | import datetime 25 | import typing 26 | import asyncio 27 | import platform 28 | 29 | # Used for default argument values 30 | _DEFAULT = object() 31 | 32 | 33 | class _UniffiRustBuffer(ctypes.Structure): 34 | _fields_ = [ 35 | ("capacity", ctypes.c_int32), 36 | ("len", ctypes.c_int32), 37 | ("data", ctypes.POINTER(ctypes.c_char)), 38 | ] 39 | 40 | @staticmethod 41 | def alloc(size): 42 | return _rust_call(_UniffiLib.ffi_op_uniffi_core_rustbuffer_alloc, size) 43 | 44 | @staticmethod 45 | def reserve(rbuf, additional): 46 | return _rust_call(_UniffiLib.ffi_op_uniffi_core_rustbuffer_reserve, rbuf, additional) 47 | 48 | def free(self): 49 | return _rust_call(_UniffiLib.ffi_op_uniffi_core_rustbuffer_free, self) 50 | 51 | def __str__(self): 52 | return "_UniffiRustBuffer(capacity={}, len={}, data={})".format( 53 | self.capacity, 54 | self.len, 55 | self.data[0:self.len] 56 | ) 57 | 58 | @contextlib.contextmanager 59 | def alloc_with_builder(*args): 60 | """Context-manger to allocate a buffer using a _UniffiRustBufferBuilder. 61 | 62 | The allocated buffer will be automatically freed if an error occurs, ensuring that 63 | we don't accidentally leak it. 64 | """ 65 | builder = _UniffiRustBufferBuilder() 66 | try: 67 | yield builder 68 | except: 69 | builder.discard() 70 | raise 71 | 72 | @contextlib.contextmanager 73 | def consume_with_stream(self): 74 | """Context-manager to consume a buffer using a _UniffiRustBufferStream. 75 | 76 | The _UniffiRustBuffer will be freed once the context-manager exits, ensuring that we don't 77 | leak it even if an error occurs. 78 | """ 79 | try: 80 | s = _UniffiRustBufferStream.from_rust_buffer(self) 81 | yield s 82 | if s.remaining() != 0: 83 | raise RuntimeError("junk data left in buffer at end of consume_with_stream") 84 | finally: 85 | self.free() 86 | 87 | @contextlib.contextmanager 88 | def read_with_stream(self): 89 | """Context-manager to read a buffer using a _UniffiRustBufferStream. 90 | 91 | This is like consume_with_stream, but doesn't free the buffer afterwards. 92 | It should only be used with borrowed `_UniffiRustBuffer` data. 93 | """ 94 | s = _UniffiRustBufferStream.from_rust_buffer(self) 95 | yield s 96 | if s.remaining() != 0: 97 | raise RuntimeError("junk data left in buffer at end of read_with_stream") 98 | 99 | class _UniffiForeignBytes(ctypes.Structure): 100 | _fields_ = [ 101 | ("len", ctypes.c_int32), 102 | ("data", ctypes.POINTER(ctypes.c_char)), 103 | ] 104 | 105 | def __str__(self): 106 | return "_UniffiForeignBytes(len={}, data={})".format(self.len, self.data[0:self.len]) 107 | 108 | 109 | class _UniffiRustBufferStream: 110 | """ 111 | Helper for structured reading of bytes from a _UniffiRustBuffer 112 | """ 113 | 114 | def __init__(self, data, len): 115 | self.data = data 116 | self.len = len 117 | self.offset = 0 118 | 119 | @classmethod 120 | def from_rust_buffer(cls, buf): 121 | return cls(buf.data, buf.len) 122 | 123 | def remaining(self): 124 | return self.len - self.offset 125 | 126 | def _unpack_from(self, size, format): 127 | if self.offset + size > self.len: 128 | raise InternalError("read past end of rust buffer") 129 | value = struct.unpack(format, self.data[self.offset:self.offset+size])[0] 130 | self.offset += size 131 | return value 132 | 133 | def read(self, size): 134 | if self.offset + size > self.len: 135 | raise InternalError("read past end of rust buffer") 136 | data = self.data[self.offset:self.offset+size] 137 | self.offset += size 138 | return data 139 | 140 | def read_i8(self): 141 | return self._unpack_from(1, ">b") 142 | 143 | def read_u8(self): 144 | return self._unpack_from(1, ">B") 145 | 146 | def read_i16(self): 147 | return self._unpack_from(2, ">h") 148 | 149 | def read_u16(self): 150 | return self._unpack_from(2, ">H") 151 | 152 | def read_i32(self): 153 | return self._unpack_from(4, ">i") 154 | 155 | def read_u32(self): 156 | return self._unpack_from(4, ">I") 157 | 158 | def read_i64(self): 159 | return self._unpack_from(8, ">q") 160 | 161 | def read_u64(self): 162 | return self._unpack_from(8, ">Q") 163 | 164 | def read_float(self): 165 | v = self._unpack_from(4, ">f") 166 | return v 167 | 168 | def read_double(self): 169 | return self._unpack_from(8, ">d") 170 | 171 | def read_c_size_t(self): 172 | return self._unpack_from(ctypes.sizeof(ctypes.c_size_t) , "@N") 173 | 174 | class _UniffiRustBufferBuilder: 175 | """ 176 | Helper for structured writing of bytes into a _UniffiRustBuffer. 177 | """ 178 | 179 | def __init__(self): 180 | self.rbuf = _UniffiRustBuffer.alloc(16) 181 | self.rbuf.len = 0 182 | 183 | def finalize(self): 184 | rbuf = self.rbuf 185 | self.rbuf = None 186 | return rbuf 187 | 188 | def discard(self): 189 | if self.rbuf is not None: 190 | rbuf = self.finalize() 191 | rbuf.free() 192 | 193 | @contextlib.contextmanager 194 | def _reserve(self, num_bytes): 195 | if self.rbuf.len + num_bytes > self.rbuf.capacity: 196 | self.rbuf = _UniffiRustBuffer.reserve(self.rbuf, num_bytes) 197 | yield None 198 | self.rbuf.len += num_bytes 199 | 200 | def _pack_into(self, size, format, value): 201 | with self._reserve(size): 202 | # XXX TODO: I feel like I should be able to use `struct.pack_into` here but can't figure it out. 203 | for i, byte in enumerate(struct.pack(format, value)): 204 | self.rbuf.data[self.rbuf.len + i] = byte 205 | 206 | def write(self, value): 207 | with self._reserve(len(value)): 208 | for i, byte in enumerate(value): 209 | self.rbuf.data[self.rbuf.len + i] = byte 210 | 211 | def write_i8(self, v): 212 | self._pack_into(1, ">b", v) 213 | 214 | def write_u8(self, v): 215 | self._pack_into(1, ">B", v) 216 | 217 | def write_i16(self, v): 218 | self._pack_into(2, ">h", v) 219 | 220 | def write_u16(self, v): 221 | self._pack_into(2, ">H", v) 222 | 223 | def write_i32(self, v): 224 | self._pack_into(4, ">i", v) 225 | 226 | def write_u32(self, v): 227 | self._pack_into(4, ">I", v) 228 | 229 | def write_i64(self, v): 230 | self._pack_into(8, ">q", v) 231 | 232 | def write_u64(self, v): 233 | self._pack_into(8, ">Q", v) 234 | 235 | def write_float(self, v): 236 | self._pack_into(4, ">f", v) 237 | 238 | def write_double(self, v): 239 | self._pack_into(8, ">d", v) 240 | 241 | def write_c_size_t(self, v): 242 | self._pack_into(ctypes.sizeof(ctypes.c_size_t) , "@N", v) 243 | # A handful of classes and functions to support the generated data structures. 244 | # This would be a good candidate for isolating in its own ffi-support lib. 245 | 246 | class InternalError(Exception): 247 | pass 248 | 249 | class _UniffiRustCallStatus(ctypes.Structure): 250 | """ 251 | Error runtime. 252 | """ 253 | _fields_ = [ 254 | ("code", ctypes.c_int8), 255 | ("error_buf", _UniffiRustBuffer), 256 | ] 257 | 258 | # These match the values from the uniffi::rustcalls module 259 | CALL_SUCCESS = 0 260 | CALL_ERROR = 1 261 | CALL_PANIC = 2 262 | 263 | def __str__(self): 264 | if self.code == _UniffiRustCallStatus.CALL_SUCCESS: 265 | return "_UniffiRustCallStatus(CALL_SUCCESS)" 266 | elif self.code == _UniffiRustCallStatus.CALL_ERROR: 267 | return "_UniffiRustCallStatus(CALL_ERROR)" 268 | elif self.code == _UniffiRustCallStatus.CALL_PANIC: 269 | return "_UniffiRustCallStatus(CALL_PANIC)" 270 | else: 271 | return "_UniffiRustCallStatus()" 272 | 273 | def _rust_call(fn, *args): 274 | # Call a rust function 275 | return _rust_call_with_error(None, fn, *args) 276 | 277 | def _rust_call_with_error(error_ffi_converter, fn, *args): 278 | # Call a rust function and handle any errors 279 | # 280 | # This function is used for rust calls that return Result<> and therefore can set the CALL_ERROR status code. 281 | # error_ffi_converter must be set to the _UniffiConverter for the error class that corresponds to the result. 282 | call_status = _UniffiRustCallStatus(code=_UniffiRustCallStatus.CALL_SUCCESS, error_buf=_UniffiRustBuffer(0, 0, None)) 283 | 284 | args_with_error = args + (ctypes.byref(call_status),) 285 | result = fn(*args_with_error) 286 | _uniffi_check_call_status(error_ffi_converter, call_status) 287 | return result 288 | 289 | def _uniffi_check_call_status(error_ffi_converter, call_status): 290 | if call_status.code == _UniffiRustCallStatus.CALL_SUCCESS: 291 | pass 292 | elif call_status.code == _UniffiRustCallStatus.CALL_ERROR: 293 | if error_ffi_converter is None: 294 | call_status.error_buf.free() 295 | raise InternalError("_rust_call_with_error: CALL_ERROR, but error_ffi_converter is None") 296 | else: 297 | raise error_ffi_converter.lift(call_status.error_buf) 298 | elif call_status.code == _UniffiRustCallStatus.CALL_PANIC: 299 | # When the rust code sees a panic, it tries to construct a _UniffiRustBuffer 300 | # with the message. But if that code panics, then it just sends back 301 | # an empty buffer. 302 | if call_status.error_buf.len > 0: 303 | msg = _UniffiConverterString.lift(call_status.error_buf) 304 | else: 305 | msg = "Unknown rust panic" 306 | raise InternalError(msg) 307 | else: 308 | raise InternalError("Invalid _UniffiRustCallStatus code: {}".format( 309 | call_status.code)) 310 | 311 | # A function pointer for a callback as defined by UniFFI. 312 | # Rust definition `fn(handle: u64, method: u32, args: _UniffiRustBuffer, buf_ptr: *mut _UniffiRustBuffer) -> int` 313 | _UNIFFI_FOREIGN_CALLBACK_T = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_ulonglong, ctypes.c_ulong, ctypes.POINTER(ctypes.c_char), ctypes.c_int, ctypes.POINTER(_UniffiRustBuffer)) 314 | 315 | # UniFFI future continuation 316 | _UNIFFI_FUTURE_CONTINUATION_T = ctypes.CFUNCTYPE(None, ctypes.c_size_t, ctypes.c_int8) 317 | 318 | class _UniffiPointerManagerCPython: 319 | """ 320 | Manage giving out pointers to Python objects on CPython 321 | 322 | This class is used to generate opaque pointers that reference Python objects to pass to Rust. 323 | It assumes a CPython platform. See _UniffiPointerManagerGeneral for the alternative. 324 | """ 325 | 326 | def new_pointer(self, obj): 327 | """ 328 | Get a pointer for an object as a ctypes.c_size_t instance 329 | 330 | Each call to new_pointer() must be balanced with exactly one call to release_pointer() 331 | 332 | This returns a ctypes.c_size_t. This is always the same size as a pointer and can be 333 | interchanged with pointers for FFI function arguments and return values. 334 | """ 335 | # IncRef the object since we're going to pass a pointer to Rust 336 | ctypes.pythonapi.Py_IncRef(ctypes.py_object(obj)) 337 | # id() is the object address on CPython 338 | # (https://docs.python.org/3/library/functions.html#id) 339 | return id(obj) 340 | 341 | def release_pointer(self, address): 342 | py_obj = ctypes.cast(address, ctypes.py_object) 343 | obj = py_obj.value 344 | ctypes.pythonapi.Py_DecRef(py_obj) 345 | return obj 346 | 347 | def lookup(self, address): 348 | return ctypes.cast(address, ctypes.py_object).value 349 | 350 | class _UniffiPointerManagerGeneral: 351 | """ 352 | Manage giving out pointers to Python objects on non-CPython platforms 353 | 354 | This has the same API as _UniffiPointerManagerCPython, but doesn't assume we're running on 355 | CPython and is slightly slower. 356 | 357 | Instead of using real pointers, it maps integer values to objects and returns the keys as 358 | c_size_t values. 359 | """ 360 | 361 | def __init__(self): 362 | self._map = {} 363 | self._lock = threading.Lock() 364 | self._current_handle = 0 365 | 366 | def new_pointer(self, obj): 367 | with self._lock: 368 | handle = self._current_handle 369 | self._current_handle += 1 370 | self._map[handle] = obj 371 | return handle 372 | 373 | def release_pointer(self, handle): 374 | with self._lock: 375 | return self._map.pop(handle) 376 | 377 | def lookup(self, handle): 378 | with self._lock: 379 | return self._map[handle] 380 | 381 | # Pick an pointer manager implementation based on the platform 382 | if platform.python_implementation() == 'CPython': 383 | _UniffiPointerManager = _UniffiPointerManagerCPython # type: ignore 384 | else: 385 | _UniffiPointerManager = _UniffiPointerManagerGeneral # type: ignore 386 | # Types conforming to `_UniffiConverterPrimitive` pass themselves directly over the FFI. 387 | class _UniffiConverterPrimitive: 388 | @classmethod 389 | def lift(cls, value): 390 | return value 391 | 392 | @classmethod 393 | def lower(cls, value): 394 | return value 395 | 396 | class _UniffiConverterPrimitiveInt(_UniffiConverterPrimitive): 397 | @classmethod 398 | def check_lower(cls, value): 399 | try: 400 | value = value.__index__() 401 | except Exception: 402 | raise TypeError("'{}' object cannot be interpreted as an integer".format(type(value).__name__)) 403 | if not isinstance(value, int): 404 | raise TypeError("__index__ returned non-int (type {})".format(type(value).__name__)) 405 | if not cls.VALUE_MIN <= value < cls.VALUE_MAX: 406 | raise ValueError("{} requires {} <= value < {}".format(cls.CLASS_NAME, cls.VALUE_MIN, cls.VALUE_MAX)) 407 | 408 | class _UniffiConverterPrimitiveFloat(_UniffiConverterPrimitive): 409 | @classmethod 410 | def check_lower(cls, value): 411 | try: 412 | value = value.__float__() 413 | except Exception: 414 | raise TypeError("must be real number, not {}".format(type(value).__name__)) 415 | if not isinstance(value, float): 416 | raise TypeError("__float__ returned non-float (type {})".format(type(value).__name__)) 417 | 418 | # Helper class for wrapper types that will always go through a _UniffiRustBuffer. 419 | # Classes should inherit from this and implement the `read` and `write` static methods. 420 | class _UniffiConverterRustBuffer: 421 | @classmethod 422 | def lift(cls, rbuf): 423 | with rbuf.consume_with_stream() as stream: 424 | return cls.read(stream) 425 | 426 | @classmethod 427 | def lower(cls, value): 428 | with _UniffiRustBuffer.alloc_with_builder() as builder: 429 | cls.write(value, builder) 430 | return builder.finalize() 431 | 432 | # Contains loading, initialization code, and the FFI Function declarations. 433 | # Define some ctypes FFI types that we use in the library 434 | 435 | """ 436 | Function pointer for a Rust task, which a callback function that takes a opaque pointer 437 | """ 438 | _UNIFFI_RUST_TASK = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_int8) 439 | 440 | def _uniffi_future_callback_t(return_type): 441 | """ 442 | Factory function to create callback function types for async functions 443 | """ 444 | return ctypes.CFUNCTYPE(None, ctypes.c_size_t, return_type, _UniffiRustCallStatus) 445 | 446 | def _uniffi_load_indirect(): 447 | """ 448 | This is how we find and load the dynamic library provided by the component. 449 | For now we just look it up by name. 450 | """ 451 | if sys.platform == "darwin": 452 | libname = "lib{}.dylib" 453 | elif sys.platform.startswith("win"): 454 | # As of python3.8, ctypes does not seem to search $PATH when loading DLLs. 455 | # We could use `os.add_dll_directory` to configure the search path, but 456 | # it doesn't feel right to mess with application-wide settings. Let's 457 | # assume that the `.dll` is next to the `.py` file and load by full path. 458 | libname = os.path.join( 459 | os.path.dirname(__file__), 460 | "{}.dll", 461 | ) 462 | else: 463 | # Anything else must be an ELF platform - Linux, *BSD, Solaris/illumos 464 | libname = "lib{}.so" 465 | 466 | libname = libname.format("op_uniffi_core") 467 | path = os.path.join(os.path.dirname(__file__), libname) 468 | lib = ctypes.cdll.LoadLibrary(path) 469 | return lib 470 | 471 | def _uniffi_check_contract_api_version(lib): 472 | # Get the bindings contract version from our ComponentInterface 473 | bindings_contract_version = 25 474 | # Get the scaffolding contract version by calling the into the dylib 475 | scaffolding_contract_version = lib.ffi_op_uniffi_core_uniffi_contract_version() 476 | if bindings_contract_version != scaffolding_contract_version: 477 | raise InternalError("UniFFI contract version mismatch: try cleaning and rebuilding your project") 478 | 479 | def _uniffi_check_api_checksums(lib): 480 | if lib.uniffi_op_uniffi_core_checksum_func_init_client() != 45066: 481 | raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") 482 | if lib.uniffi_op_uniffi_core_checksum_func_invoke() != 29143: 483 | raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") 484 | if lib.uniffi_op_uniffi_core_checksum_func_invoke_sync() != 49373: 485 | raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") 486 | if lib.uniffi_op_uniffi_core_checksum_func_release_client() != 57155: 487 | raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") 488 | 489 | # A ctypes library to expose the extern-C FFI definitions. 490 | # This is an implementation detail which will be called internally by the public API. 491 | 492 | _UniffiLib = _uniffi_load_indirect() 493 | _UniffiLib.uniffi_op_uniffi_core_fn_func_init_client.argtypes = ( 494 | _UniffiRustBuffer, 495 | ) 496 | _UniffiLib.uniffi_op_uniffi_core_fn_func_init_client.restype = ctypes.c_void_p 497 | _UniffiLib.uniffi_op_uniffi_core_fn_func_invoke.argtypes = ( 498 | _UniffiRustBuffer, 499 | ) 500 | _UniffiLib.uniffi_op_uniffi_core_fn_func_invoke.restype = ctypes.c_void_p 501 | _UniffiLib.uniffi_op_uniffi_core_fn_func_invoke_sync.argtypes = ( 502 | _UniffiRustBuffer, 503 | ctypes.POINTER(_UniffiRustCallStatus), 504 | ) 505 | _UniffiLib.uniffi_op_uniffi_core_fn_func_invoke_sync.restype = _UniffiRustBuffer 506 | _UniffiLib.uniffi_op_uniffi_core_fn_func_release_client.argtypes = ( 507 | _UniffiRustBuffer, 508 | ctypes.POINTER(_UniffiRustCallStatus), 509 | ) 510 | _UniffiLib.uniffi_op_uniffi_core_fn_func_release_client.restype = None 511 | _UniffiLib.ffi_op_uniffi_core_rustbuffer_alloc.argtypes = ( 512 | ctypes.c_int32, 513 | ctypes.POINTER(_UniffiRustCallStatus), 514 | ) 515 | _UniffiLib.ffi_op_uniffi_core_rustbuffer_alloc.restype = _UniffiRustBuffer 516 | _UniffiLib.ffi_op_uniffi_core_rustbuffer_from_bytes.argtypes = ( 517 | _UniffiForeignBytes, 518 | ctypes.POINTER(_UniffiRustCallStatus), 519 | ) 520 | _UniffiLib.ffi_op_uniffi_core_rustbuffer_from_bytes.restype = _UniffiRustBuffer 521 | _UniffiLib.ffi_op_uniffi_core_rustbuffer_free.argtypes = ( 522 | _UniffiRustBuffer, 523 | ctypes.POINTER(_UniffiRustCallStatus), 524 | ) 525 | _UniffiLib.ffi_op_uniffi_core_rustbuffer_free.restype = None 526 | _UniffiLib.ffi_op_uniffi_core_rustbuffer_reserve.argtypes = ( 527 | _UniffiRustBuffer, 528 | ctypes.c_int32, 529 | ctypes.POINTER(_UniffiRustCallStatus), 530 | ) 531 | _UniffiLib.ffi_op_uniffi_core_rustbuffer_reserve.restype = _UniffiRustBuffer 532 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_u8.argtypes = ( 533 | ctypes.c_void_p, 534 | _UNIFFI_FUTURE_CONTINUATION_T, 535 | ctypes.c_size_t, 536 | ) 537 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_u8.restype = None 538 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u8.argtypes = ( 539 | ctypes.c_void_p, 540 | ) 541 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u8.restype = None 542 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_u8.argtypes = ( 543 | ctypes.c_void_p, 544 | ) 545 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_u8.restype = None 546 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_u8.argtypes = ( 547 | ctypes.c_void_p, 548 | ctypes.POINTER(_UniffiRustCallStatus), 549 | ) 550 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_u8.restype = ctypes.c_uint8 551 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_i8.argtypes = ( 552 | ctypes.c_void_p, 553 | _UNIFFI_FUTURE_CONTINUATION_T, 554 | ctypes.c_size_t, 555 | ) 556 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_i8.restype = None 557 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i8.argtypes = ( 558 | ctypes.c_void_p, 559 | ) 560 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i8.restype = None 561 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_i8.argtypes = ( 562 | ctypes.c_void_p, 563 | ) 564 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_i8.restype = None 565 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_i8.argtypes = ( 566 | ctypes.c_void_p, 567 | ctypes.POINTER(_UniffiRustCallStatus), 568 | ) 569 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_i8.restype = ctypes.c_int8 570 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_u16.argtypes = ( 571 | ctypes.c_void_p, 572 | _UNIFFI_FUTURE_CONTINUATION_T, 573 | ctypes.c_size_t, 574 | ) 575 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_u16.restype = None 576 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u16.argtypes = ( 577 | ctypes.c_void_p, 578 | ) 579 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u16.restype = None 580 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_u16.argtypes = ( 581 | ctypes.c_void_p, 582 | ) 583 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_u16.restype = None 584 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_u16.argtypes = ( 585 | ctypes.c_void_p, 586 | ctypes.POINTER(_UniffiRustCallStatus), 587 | ) 588 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_u16.restype = ctypes.c_uint16 589 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_i16.argtypes = ( 590 | ctypes.c_void_p, 591 | _UNIFFI_FUTURE_CONTINUATION_T, 592 | ctypes.c_size_t, 593 | ) 594 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_i16.restype = None 595 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i16.argtypes = ( 596 | ctypes.c_void_p, 597 | ) 598 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i16.restype = None 599 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_i16.argtypes = ( 600 | ctypes.c_void_p, 601 | ) 602 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_i16.restype = None 603 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_i16.argtypes = ( 604 | ctypes.c_void_p, 605 | ctypes.POINTER(_UniffiRustCallStatus), 606 | ) 607 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_i16.restype = ctypes.c_int16 608 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_u32.argtypes = ( 609 | ctypes.c_void_p, 610 | _UNIFFI_FUTURE_CONTINUATION_T, 611 | ctypes.c_size_t, 612 | ) 613 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_u32.restype = None 614 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u32.argtypes = ( 615 | ctypes.c_void_p, 616 | ) 617 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u32.restype = None 618 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_u32.argtypes = ( 619 | ctypes.c_void_p, 620 | ) 621 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_u32.restype = None 622 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_u32.argtypes = ( 623 | ctypes.c_void_p, 624 | ctypes.POINTER(_UniffiRustCallStatus), 625 | ) 626 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_u32.restype = ctypes.c_uint32 627 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_i32.argtypes = ( 628 | ctypes.c_void_p, 629 | _UNIFFI_FUTURE_CONTINUATION_T, 630 | ctypes.c_size_t, 631 | ) 632 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_i32.restype = None 633 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i32.argtypes = ( 634 | ctypes.c_void_p, 635 | ) 636 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i32.restype = None 637 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_i32.argtypes = ( 638 | ctypes.c_void_p, 639 | ) 640 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_i32.restype = None 641 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_i32.argtypes = ( 642 | ctypes.c_void_p, 643 | ctypes.POINTER(_UniffiRustCallStatus), 644 | ) 645 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_i32.restype = ctypes.c_int32 646 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_u64.argtypes = ( 647 | ctypes.c_void_p, 648 | _UNIFFI_FUTURE_CONTINUATION_T, 649 | ctypes.c_size_t, 650 | ) 651 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_u64.restype = None 652 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u64.argtypes = ( 653 | ctypes.c_void_p, 654 | ) 655 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u64.restype = None 656 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_u64.argtypes = ( 657 | ctypes.c_void_p, 658 | ) 659 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_u64.restype = None 660 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_u64.argtypes = ( 661 | ctypes.c_void_p, 662 | ctypes.POINTER(_UniffiRustCallStatus), 663 | ) 664 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_u64.restype = ctypes.c_uint64 665 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_i64.argtypes = ( 666 | ctypes.c_void_p, 667 | _UNIFFI_FUTURE_CONTINUATION_T, 668 | ctypes.c_size_t, 669 | ) 670 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_i64.restype = None 671 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i64.argtypes = ( 672 | ctypes.c_void_p, 673 | ) 674 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i64.restype = None 675 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_i64.argtypes = ( 676 | ctypes.c_void_p, 677 | ) 678 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_i64.restype = None 679 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_i64.argtypes = ( 680 | ctypes.c_void_p, 681 | ctypes.POINTER(_UniffiRustCallStatus), 682 | ) 683 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_i64.restype = ctypes.c_int64 684 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_f32.argtypes = ( 685 | ctypes.c_void_p, 686 | _UNIFFI_FUTURE_CONTINUATION_T, 687 | ctypes.c_size_t, 688 | ) 689 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_f32.restype = None 690 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_f32.argtypes = ( 691 | ctypes.c_void_p, 692 | ) 693 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_f32.restype = None 694 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_f32.argtypes = ( 695 | ctypes.c_void_p, 696 | ) 697 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_f32.restype = None 698 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_f32.argtypes = ( 699 | ctypes.c_void_p, 700 | ctypes.POINTER(_UniffiRustCallStatus), 701 | ) 702 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_f32.restype = ctypes.c_float 703 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_f64.argtypes = ( 704 | ctypes.c_void_p, 705 | _UNIFFI_FUTURE_CONTINUATION_T, 706 | ctypes.c_size_t, 707 | ) 708 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_f64.restype = None 709 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_f64.argtypes = ( 710 | ctypes.c_void_p, 711 | ) 712 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_f64.restype = None 713 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_f64.argtypes = ( 714 | ctypes.c_void_p, 715 | ) 716 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_f64.restype = None 717 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_f64.argtypes = ( 718 | ctypes.c_void_p, 719 | ctypes.POINTER(_UniffiRustCallStatus), 720 | ) 721 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_f64.restype = ctypes.c_double 722 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_pointer.argtypes = ( 723 | ctypes.c_void_p, 724 | _UNIFFI_FUTURE_CONTINUATION_T, 725 | ctypes.c_size_t, 726 | ) 727 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_pointer.restype = None 728 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_pointer.argtypes = ( 729 | ctypes.c_void_p, 730 | ) 731 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_pointer.restype = None 732 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_pointer.argtypes = ( 733 | ctypes.c_void_p, 734 | ) 735 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_pointer.restype = None 736 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_pointer.argtypes = ( 737 | ctypes.c_void_p, 738 | ctypes.POINTER(_UniffiRustCallStatus), 739 | ) 740 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_pointer.restype = ctypes.c_void_p 741 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_rust_buffer.argtypes = ( 742 | ctypes.c_void_p, 743 | _UNIFFI_FUTURE_CONTINUATION_T, 744 | ctypes.c_size_t, 745 | ) 746 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_rust_buffer.restype = None 747 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_rust_buffer.argtypes = ( 748 | ctypes.c_void_p, 749 | ) 750 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_rust_buffer.restype = None 751 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_rust_buffer.argtypes = ( 752 | ctypes.c_void_p, 753 | ) 754 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_rust_buffer.restype = None 755 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_rust_buffer.argtypes = ( 756 | ctypes.c_void_p, 757 | ctypes.POINTER(_UniffiRustCallStatus), 758 | ) 759 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_rust_buffer.restype = _UniffiRustBuffer 760 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_void.argtypes = ( 761 | ctypes.c_void_p, 762 | _UNIFFI_FUTURE_CONTINUATION_T, 763 | ctypes.c_size_t, 764 | ) 765 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_void.restype = None 766 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_void.argtypes = ( 767 | ctypes.c_void_p, 768 | ) 769 | _UniffiLib.ffi_op_uniffi_core_rust_future_cancel_void.restype = None 770 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_void.argtypes = ( 771 | ctypes.c_void_p, 772 | ) 773 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_void.restype = None 774 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_void.argtypes = ( 775 | ctypes.c_void_p, 776 | ctypes.POINTER(_UniffiRustCallStatus), 777 | ) 778 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_void.restype = None 779 | _UniffiLib.uniffi_op_uniffi_core_checksum_func_init_client.argtypes = ( 780 | ) 781 | _UniffiLib.uniffi_op_uniffi_core_checksum_func_init_client.restype = ctypes.c_uint16 782 | _UniffiLib.uniffi_op_uniffi_core_checksum_func_invoke.argtypes = ( 783 | ) 784 | _UniffiLib.uniffi_op_uniffi_core_checksum_func_invoke.restype = ctypes.c_uint16 785 | _UniffiLib.uniffi_op_uniffi_core_checksum_func_invoke_sync.argtypes = ( 786 | ) 787 | _UniffiLib.uniffi_op_uniffi_core_checksum_func_invoke_sync.restype = ctypes.c_uint16 788 | _UniffiLib.uniffi_op_uniffi_core_checksum_func_release_client.argtypes = ( 789 | ) 790 | _UniffiLib.uniffi_op_uniffi_core_checksum_func_release_client.restype = ctypes.c_uint16 791 | _UniffiLib.ffi_op_uniffi_core_uniffi_contract_version.argtypes = ( 792 | ) 793 | _UniffiLib.ffi_op_uniffi_core_uniffi_contract_version.restype = ctypes.c_uint32 794 | _uniffi_check_contract_api_version(_UniffiLib) 795 | _uniffi_check_api_checksums(_UniffiLib) 796 | 797 | # Async support# RustFuturePoll values 798 | _UNIFFI_RUST_FUTURE_POLL_READY = 0 799 | _UNIFFI_RUST_FUTURE_POLL_MAYBE_READY = 1 800 | 801 | # Stores futures for _uniffi_continuation_callback 802 | _UniffiContinuationPointerManager = _UniffiPointerManager() 803 | 804 | # Continuation callback for async functions 805 | # lift the return value or error and resolve the future, causing the async function to resume. 806 | @_UNIFFI_FUTURE_CONTINUATION_T 807 | def _uniffi_continuation_callback(future_ptr, poll_code): 808 | (eventloop, future) = _UniffiContinuationPointerManager.release_pointer(future_ptr) 809 | eventloop.call_soon_threadsafe(_uniffi_set_future_result, future, poll_code) 810 | 811 | def _uniffi_set_future_result(future, poll_code): 812 | if not future.cancelled(): 813 | future.set_result(poll_code) 814 | 815 | async def _uniffi_rust_call_async(rust_future, ffi_poll, ffi_complete, ffi_free, lift_func, error_ffi_converter): 816 | try: 817 | eventloop = asyncio.get_running_loop() 818 | 819 | # Loop and poll until we see a _UNIFFI_RUST_FUTURE_POLL_READY value 820 | while True: 821 | future = eventloop.create_future() 822 | ffi_poll( 823 | rust_future, 824 | _uniffi_continuation_callback, 825 | _UniffiContinuationPointerManager.new_pointer((eventloop, future)), 826 | ) 827 | poll_code = await future 828 | if poll_code == _UNIFFI_RUST_FUTURE_POLL_READY: 829 | break 830 | 831 | return lift_func( 832 | _rust_call_with_error(error_ffi_converter, ffi_complete, rust_future) 833 | ) 834 | finally: 835 | ffi_free(rust_future) 836 | 837 | # Public interface members begin here. 838 | 839 | 840 | class _UniffiConverterString: 841 | @staticmethod 842 | def check_lower(value): 843 | if not isinstance(value, str): 844 | raise TypeError("argument must be str, not {}".format(type(value).__name__)) 845 | return value 846 | 847 | @staticmethod 848 | def read(buf): 849 | size = buf.read_i32() 850 | if size < 0: 851 | raise InternalError("Unexpected negative string length") 852 | utf8_bytes = buf.read(size) 853 | return utf8_bytes.decode("utf-8") 854 | 855 | @staticmethod 856 | def write(value, buf): 857 | utf8_bytes = value.encode("utf-8") 858 | buf.write_i32(len(utf8_bytes)) 859 | buf.write(utf8_bytes) 860 | 861 | @staticmethod 862 | def lift(buf): 863 | with buf.consume_with_stream() as stream: 864 | return stream.read(stream.remaining()).decode("utf-8") 865 | 866 | @staticmethod 867 | def lower(value): 868 | with _UniffiRustBuffer.alloc_with_builder() as builder: 869 | builder.write(value.encode("utf-8")) 870 | return builder.finalize() 871 | 872 | 873 | # Error 874 | # We want to define each variant as a nested class that's also a subclass, 875 | # which is tricky in Python. To accomplish this we're going to create each 876 | # class separately, then manually add the child classes to the base class's 877 | # __dict__. All of this happens in dummy class to avoid polluting the module 878 | # namespace. 879 | class Error(Exception): 880 | """ 881 | Error type sent over the FFI by UniFFI. 882 | 883 | `uniffi::Error` only supports errors that are enums, so we need to have a single-variant enum here. 884 | """ 885 | 886 | pass 887 | 888 | _UniffiTempError = Error 889 | 890 | class Error: # type: ignore 891 | class Error(_UniffiTempError): 892 | """ 893 | Any error ocurring in the SDK 894 | """ 895 | 896 | 897 | def __init__(self, msg): 898 | super().__init__(", ".join([ 899 | "msg={!r}".format(msg), 900 | ])) 901 | self.msg = msg 902 | def __repr__(self): 903 | return "Error.Error({})".format(str(self)) 904 | _UniffiTempError.Error = Error # type: ignore 905 | 906 | Error = _UniffiTempError # type: ignore 907 | del _UniffiTempError 908 | 909 | 910 | class _UniffiConverterTypeError(_UniffiConverterRustBuffer): 911 | @staticmethod 912 | def read(buf): 913 | variant = buf.read_i32() 914 | if variant == 1: 915 | return Error.Error( 916 | msg=_UniffiConverterString.read(buf), 917 | ) 918 | raise InternalError("Raw enum value doesn't match any cases") 919 | 920 | @staticmethod 921 | def check_lower(value): 922 | if isinstance(value, Error.Error): 923 | _UniffiConverterString.check_lower(value.msg) 924 | return 925 | 926 | @staticmethod 927 | def write(value, buf): 928 | if isinstance(value, Error.Error): 929 | buf.write_i32(1) 930 | _UniffiConverterString.write(value.msg, buf) 931 | 932 | def init_client(client_config: "str"): 933 | _UniffiConverterString.check_lower(client_config) 934 | 935 | return _uniffi_rust_call_async( 936 | _UniffiLib.uniffi_op_uniffi_core_fn_func_init_client( 937 | _UniffiConverterString.lower(client_config)), 938 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_rust_buffer, 939 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_rust_buffer, 940 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_rust_buffer, 941 | # lift function 942 | _UniffiConverterString.lift, 943 | # Error FFI converter 944 | _UniffiConverterTypeError, 945 | ) 946 | 947 | def invoke(invocation: "str"): 948 | _UniffiConverterString.check_lower(invocation) 949 | 950 | return _uniffi_rust_call_async( 951 | _UniffiLib.uniffi_op_uniffi_core_fn_func_invoke( 952 | _UniffiConverterString.lower(invocation)), 953 | _UniffiLib.ffi_op_uniffi_core_rust_future_poll_rust_buffer, 954 | _UniffiLib.ffi_op_uniffi_core_rust_future_complete_rust_buffer, 955 | _UniffiLib.ffi_op_uniffi_core_rust_future_free_rust_buffer, 956 | # lift function 957 | _UniffiConverterString.lift, 958 | # Error FFI converter 959 | _UniffiConverterTypeError, 960 | ) 961 | 962 | def invoke_sync(invocation: "str") -> "str": 963 | _UniffiConverterString.check_lower(invocation) 964 | 965 | return _UniffiConverterString.lift(_rust_call_with_error(_UniffiConverterTypeError,_UniffiLib.uniffi_op_uniffi_core_fn_func_invoke_sync, 966 | _UniffiConverterString.lower(invocation))) 967 | 968 | 969 | def release_client(client_id: "str"): 970 | _UniffiConverterString.check_lower(client_id) 971 | 972 | _rust_call_with_error(_UniffiConverterTypeError,_UniffiLib.uniffi_op_uniffi_core_fn_func_release_client, 973 | _UniffiConverterString.lower(client_id)) 974 | 975 | 976 | __all__ = [ 977 | "InternalError", 978 | "Error", 979 | "init_client", 980 | "invoke", 981 | "invoke_sync", 982 | "release_client", 983 | ] 984 | 985 | -------------------------------------------------------------------------------- /src/onepassword/secrets.py: -------------------------------------------------------------------------------- 1 | # Code generated by op-codegen - DO NO EDIT MANUALLY 2 | 3 | from .core import _invoke, _invoke_sync 4 | from typing import Optional, List 5 | from pydantic import TypeAdapter 6 | from .types import GeneratePasswordResponse, PasswordRecipe, ResolveAllResponse 7 | 8 | 9 | class Secrets: 10 | """ 11 | The Secrets API includes all operations the SDK client can perform on secrets. 12 | Use secret reference URIs to securely load secrets from 1Password: op:///[/]/ 13 | """ 14 | 15 | def __init__(self, client_id): 16 | self.client_id = client_id 17 | 18 | async def resolve(self, secret_reference: str) -> str: 19 | """ 20 | Resolve returns the secret the provided secret reference points to. 21 | """ 22 | response = await _invoke( 23 | { 24 | "invocation": { 25 | "clientId": self.client_id, 26 | "parameters": { 27 | "name": "SecretsResolve", 28 | "parameters": {"secret_reference": secret_reference}, 29 | }, 30 | } 31 | } 32 | ) 33 | 34 | response = TypeAdapter(str).validate_json(response) 35 | return response 36 | 37 | async def resolve_all(self, secret_references: List[str]) -> ResolveAllResponse: 38 | """ 39 | Resolve takes in a list of secret references and returns the secrets they point to or errors if any. 40 | """ 41 | response = await _invoke( 42 | { 43 | "invocation": { 44 | "clientId": self.client_id, 45 | "parameters": { 46 | "name": "SecretsResolveAll", 47 | "parameters": {"secret_references": secret_references}, 48 | }, 49 | } 50 | } 51 | ) 52 | 53 | response = TypeAdapter(ResolveAllResponse).validate_json(response) 54 | return response 55 | 56 | @staticmethod 57 | def validate_secret_reference(secret_reference: str) -> None: 58 | """ 59 | Validate the secret reference to ensure there are no syntax errors. 60 | """ 61 | response = _invoke_sync( 62 | { 63 | "invocation": { 64 | "parameters": { 65 | "name": "ValidateSecretReference", 66 | "parameters": {"secret_reference": secret_reference}, 67 | } 68 | } 69 | } 70 | ) 71 | 72 | return None 73 | 74 | @staticmethod 75 | def generate_password(recipe: PasswordRecipe) -> GeneratePasswordResponse: 76 | response = _invoke_sync( 77 | { 78 | "invocation": { 79 | "parameters": { 80 | "name": "GeneratePassword", 81 | "parameters": {"recipe": recipe.model_dump(by_alias=True)}, 82 | } 83 | } 84 | } 85 | ) 86 | 87 | response = TypeAdapter(GeneratePasswordResponse).validate_json(response) 88 | return response 89 | -------------------------------------------------------------------------------- /src/onepassword/test_client.py: -------------------------------------------------------------------------------- 1 | import onepassword.client as onepassword 2 | import onepassword.defaults as onepassword_defaults 3 | import os 4 | import platform 5 | import pytest 6 | 7 | TOKEN = os.getenv("OP_SERVICE_ACCOUNT_TOKEN") 8 | 9 | ## test resolve function 10 | 11 | 12 | # valid 13 | @pytest.mark.asyncio 14 | async def test_valid_resolve(): 15 | client = await onepassword.Client.authenticate( 16 | auth=TOKEN, 17 | integration_name=onepassword_defaults.DEFAULT_INTEGRATION_NAME, 18 | integration_version=onepassword_defaults.DEFAULT_INTEGRATION_VERSION, 19 | ) 20 | result = await client.secrets.resolve( 21 | secret_reference="op://gowwbvgow7kxocrfmfvtwni6vi/6ydrn7ne6mwnqc2prsbqx4i4aq/password" 22 | ) 23 | assert result == "test_password_42" 24 | 25 | 26 | # invalid 27 | @pytest.mark.asyncio 28 | async def test_invalid_resolve(): 29 | client = await onepassword.Client.authenticate( 30 | auth=TOKEN, 31 | integration_name=onepassword_defaults.DEFAULT_INTEGRATION_NAME, 32 | integration_version=onepassword_defaults.DEFAULT_INTEGRATION_VERSION, 33 | ) 34 | with pytest.raises( 35 | Exception, 36 | match='error resolving secret reference: the secret reference could not be parsed: secret reference is not prefixed with "op://"', 37 | ): 38 | await client.secrets.resolve(secret_reference="invalid_reference") 39 | 40 | 41 | ## test client constructor 42 | 43 | 44 | # invalid 45 | @pytest.mark.asyncio 46 | async def test_client_construction_no_auth(): 47 | with pytest.raises( 48 | Exception, 49 | match="invalid user input: encountered the following errors: service account token was not specified", 50 | ): 51 | await onepassword.Client.authenticate( 52 | auth="", 53 | integration_name=onepassword_defaults.DEFAULT_INTEGRATION_NAME, 54 | integration_version=onepassword_defaults.DEFAULT_INTEGRATION_VERSION, 55 | ) 56 | 57 | 58 | # invalid 59 | @pytest.mark.asyncio 60 | async def test_client_construction_no_name(): 61 | with pytest.raises( 62 | Exception, 63 | match="invalid user input: encountered the following errors: integration name was not specified", 64 | ): 65 | await onepassword.Client.authenticate( 66 | auth=TOKEN, 67 | integration_name="", 68 | integration_version=onepassword_defaults.DEFAULT_INTEGRATION_VERSION, 69 | ) 70 | 71 | 72 | # invalid 73 | @pytest.mark.asyncio 74 | async def test_client_construction_no_version(): 75 | with pytest.raises( 76 | Exception, 77 | match="invalid user input: encountered the following errors: integration version was not specified", 78 | ): 79 | await onepassword.Client.authenticate( 80 | auth=TOKEN, 81 | integration_name=onepassword_defaults.DEFAULT_INTEGRATION_NAME, 82 | integration_version="", 83 | ) 84 | 85 | 86 | ## test config function 87 | 88 | 89 | # valid 90 | def test_good_new_onepassword_default_config(): 91 | config = onepassword.new_default_config( 92 | auth=TOKEN, 93 | integration_name=onepassword_defaults.DEFAULT_INTEGRATION_NAME, 94 | integration_version=onepassword_defaults.DEFAULT_INTEGRATION_VERSION, 95 | ) 96 | 97 | assert config["serviceAccountToken"] == TOKEN 98 | assert config["programmingLanguage"] == onepassword_defaults.SDK_LANGUAGE 99 | assert config["sdkVersion"] == onepassword_defaults.SDK_VERSION 100 | assert config["integrationName"] == onepassword_defaults.DEFAULT_INTEGRATION_NAME 101 | assert ( 102 | config["integrationVersion"] == onepassword_defaults.DEFAULT_INTEGRATION_VERSION 103 | ) 104 | assert config["requestLibraryName"] == onepassword_defaults.DEFAULT_REQUEST_LIBRARY 105 | assert ( 106 | config["requestLibraryVersion"] 107 | == onepassword_defaults.DEFAULT_REQUEST_LIBRARY_VERSION 108 | ) 109 | assert config["os"] == platform.system().lower() 110 | assert config["osVersion"] == onepassword_defaults.DEFAULT_OS_VERSION 111 | assert config["architecture"] == platform.machine() 112 | -------------------------------------------------------------------------------- /src/onepassword/types.py: -------------------------------------------------------------------------------- 1 | """ 2 | Generated by typeshare 1.13.2 3 | """ 4 | 5 | from __future__ import annotations 6 | 7 | from datetime import datetime 8 | from enum import Enum 9 | from pydantic import BaseModel, BeforeValidator, ConfigDict, Field, PlainSerializer 10 | from typing import Annotated, Dict, Generic, List, Literal, Optional, TypeVar, Union 11 | 12 | E = TypeVar("E") 13 | T = TypeVar("T") 14 | 15 | 16 | def serialize_binary_data(value: bytes) -> list[int]: 17 | return list(value) 18 | 19 | 20 | def deserialize_binary_data(value): 21 | if isinstance(value, list): 22 | if all(isinstance(x, int) and 0 <= x <= 255 for x in value): 23 | return bytes(value) 24 | raise ValueError("All elements must be integers in the range 0-255 (u8).") 25 | elif isinstance(value, bytes): 26 | return value 27 | raise TypeError("Content must be a list of integers (0-255) or bytes.") 28 | 29 | 30 | def serialize_datetime_data(utc_time: datetime) -> str: 31 | return utc_time.strftime("%Y-%m-%dT%H:%M:%S.%fZ") 32 | 33 | 34 | def parse_rfc3339(date_str: str) -> datetime: 35 | date_formats = ["%Y-%m-%dT%H:%M:%SZ", "%Y-%m-%dT%H:%M:%S.%fZ"] 36 | 37 | for fmt in date_formats: 38 | try: 39 | return datetime.strptime(date_str, fmt) 40 | except ValueError: 41 | continue 42 | 43 | raise ValueError(f"Invalid RFC 3339 date format: {date_str}") 44 | 45 | 46 | ErrorMessage = str 47 | 48 | 49 | class AddressFieldDetails(BaseModel): 50 | """ 51 | Additional attributes for OTP fields. 52 | """ 53 | 54 | street: str 55 | """ 56 | The street address 57 | """ 58 | city: str 59 | """ 60 | The city 61 | """ 62 | country: str 63 | """ 64 | The country 65 | """ 66 | zip: str 67 | """ 68 | The ZIP code 69 | """ 70 | state: str 71 | """ 72 | The state 73 | """ 74 | 75 | 76 | class DocumentCreateParams(BaseModel): 77 | name: str 78 | """ 79 | The name of the file 80 | """ 81 | content: Annotated[ 82 | bytes, 83 | BeforeValidator(deserialize_binary_data), 84 | PlainSerializer(serialize_binary_data), 85 | ] 86 | """ 87 | The content of the file 88 | """ 89 | 90 | 91 | class FileAttributes(BaseModel): 92 | name: str 93 | """ 94 | The name of the file 95 | """ 96 | id: str 97 | """ 98 | The ID of the file retrieved from the server 99 | """ 100 | size: int 101 | """ 102 | The size of the file in bytes 103 | """ 104 | 105 | 106 | class FileCreateParams(BaseModel): 107 | model_config = ConfigDict(populate_by_name=True) 108 | 109 | name: str 110 | """ 111 | The name of the file 112 | """ 113 | content: Annotated[ 114 | bytes, 115 | BeforeValidator(deserialize_binary_data), 116 | PlainSerializer(serialize_binary_data), 117 | ] 118 | """ 119 | The content of the file 120 | """ 121 | section_id: str = Field(alias="sectionId") 122 | """ 123 | The section id where the file should be stored 124 | """ 125 | field_id: str = Field(alias="fieldId") 126 | """ 127 | The field id where the file should be stored 128 | """ 129 | 130 | 131 | class GeneratePasswordResponse(BaseModel): 132 | """ 133 | For future use, if we want to return more information about the generated password. 134 | Currently, it only returns the password itself. 135 | """ 136 | 137 | password: str 138 | """ 139 | The generated password. 140 | """ 141 | 142 | 143 | class ItemCategory(str, Enum): 144 | LOGIN = "Login" 145 | SECURENOTE = "SecureNote" 146 | CREDITCARD = "CreditCard" 147 | CRYPTOWALLET = "CryptoWallet" 148 | IDENTITY = "Identity" 149 | PASSWORD = "Password" 150 | DOCUMENT = "Document" 151 | APICREDENTIALS = "ApiCredentials" 152 | BANKACCOUNT = "BankAccount" 153 | DATABASE = "Database" 154 | DRIVERLICENSE = "DriverLicense" 155 | EMAIL = "Email" 156 | MEDICALRECORD = "MedicalRecord" 157 | MEMBERSHIP = "Membership" 158 | OUTDOORLICENSE = "OutdoorLicense" 159 | PASSPORT = "Passport" 160 | REWARDS = "Rewards" 161 | ROUTER = "Router" 162 | SERVER = "Server" 163 | SSHKEY = "SshKey" 164 | SOCIALSECURITYNUMBER = "SocialSecurityNumber" 165 | SOFTWARELICENSE = "SoftwareLicense" 166 | PERSON = "Person" 167 | UNSUPPORTED = "Unsupported" 168 | 169 | 170 | class ItemFieldType(str, Enum): 171 | TEXT = "Text" 172 | CONCEALED = "Concealed" 173 | CREDITCARDTYPE = "CreditCardType" 174 | CREDITCARDNUMBER = "CreditCardNumber" 175 | PHONE = "Phone" 176 | URL = "Url" 177 | TOTP = "Totp" 178 | EMAIL = "Email" 179 | REFERENCE = "Reference" 180 | SSHKEY = "SshKey" 181 | MENU = "Menu" 182 | MONTHYEAR = "MonthYear" 183 | ADDRESS = "Address" 184 | DATE = "Date" 185 | UNSUPPORTED = "Unsupported" 186 | 187 | 188 | class ItemFieldDetailsTypes(str, Enum): 189 | OTP = "Otp" 190 | SSH_KEY = "SshKey" 191 | ADDRESS = "Address" 192 | 193 | 194 | class ItemFieldDetailsOtp(BaseModel): 195 | """ 196 | The computed OTP code and other details 197 | """ 198 | 199 | type: Literal[ItemFieldDetailsTypes.OTP] = ItemFieldDetailsTypes.OTP 200 | content: OtpFieldDetails 201 | 202 | 203 | class ItemFieldDetailsSshKey(BaseModel): 204 | """ 205 | Computed SSH Key attributes 206 | """ 207 | 208 | type: Literal[ItemFieldDetailsTypes.SSH_KEY] = ItemFieldDetailsTypes.SSH_KEY 209 | content: Optional[SshKeyAttributes] 210 | 211 | 212 | class ItemFieldDetailsAddress(BaseModel): 213 | """ 214 | Address components 215 | """ 216 | 217 | type: Literal[ItemFieldDetailsTypes.ADDRESS] = ItemFieldDetailsTypes.ADDRESS 218 | content: Optional[AddressFieldDetails] 219 | 220 | 221 | # Field type-specific attributes. 222 | ItemFieldDetails = Union[ 223 | ItemFieldDetailsOtp, ItemFieldDetailsSshKey, ItemFieldDetailsAddress 224 | ] 225 | 226 | 227 | class ItemField(BaseModel): 228 | """ 229 | Represents a field within an item. 230 | """ 231 | 232 | model_config = ConfigDict(populate_by_name=True) 233 | 234 | id: str 235 | """ 236 | The field's ID 237 | """ 238 | title: str 239 | """ 240 | The field's title 241 | """ 242 | section_id: Optional[str] = Field(alias="sectionId", default=None) 243 | """ 244 | The ID of the section containing the field. Built-in fields such as usernames and passwords don't require a section. 245 | """ 246 | field_type: ItemFieldType = Field(alias="fieldType") 247 | """ 248 | The field's type 249 | """ 250 | value: str 251 | """ 252 | The string representation of the field's value 253 | """ 254 | details: Optional[ItemFieldDetails] = Field(default=None) 255 | """ 256 | Field type-specific attributes. 257 | """ 258 | 259 | 260 | class ItemSection(BaseModel): 261 | """ 262 | A section groups together multiple fields in an item. 263 | """ 264 | 265 | id: str 266 | """ 267 | The section's unique ID 268 | """ 269 | title: str 270 | """ 271 | The section's title 272 | """ 273 | 274 | 275 | class AutofillBehavior(str, Enum): 276 | """ 277 | Controls the auto-fill behavior of a website. 278 | 279 | 280 | For more information, visit https://support.1password.com/autofill-behavior/ 281 | """ 282 | 283 | ANYWHEREONWEBSITE = "AnywhereOnWebsite" 284 | """ 285 | Auto-fill any page that’s part of the website, including subdomains 286 | """ 287 | EXACTDOMAIN = "ExactDomain" 288 | """ 289 | Auto-fill only if the domain (hostname and port) is an exact match. 290 | """ 291 | NEVER = "Never" 292 | """ 293 | Never auto-fill on this website 294 | """ 295 | 296 | 297 | class Website(BaseModel): 298 | model_config = ConfigDict(populate_by_name=True) 299 | 300 | url: str 301 | """ 302 | The website URL 303 | """ 304 | label: str 305 | """ 306 | The label of the website, e.g. 'website', 'sign-in address' 307 | """ 308 | autofill_behavior: AutofillBehavior = Field(alias="autofillBehavior") 309 | """ 310 | The auto-fill behavior of the website 311 | 312 | For more information, visit https://support.1password.com/autofill-behavior/ 313 | """ 314 | 315 | 316 | class ItemFile(BaseModel): 317 | model_config = ConfigDict(populate_by_name=True) 318 | 319 | attributes: FileAttributes 320 | """ 321 | the attributes of the file 322 | """ 323 | section_id: str = Field(alias="sectionId") 324 | """ 325 | the section id where the file should be stored 326 | """ 327 | field_id: str = Field(alias="fieldId") 328 | """ 329 | the field id where the file should be stored 330 | """ 331 | 332 | 333 | class Item(BaseModel): 334 | """ 335 | Represents an active 1Password item. 336 | """ 337 | 338 | model_config = ConfigDict(populate_by_name=True) 339 | 340 | id: str 341 | """ 342 | The item's ID 343 | """ 344 | title: str 345 | """ 346 | The item's title 347 | """ 348 | category: ItemCategory 349 | """ 350 | The item's category 351 | """ 352 | vault_id: str = Field(alias="vaultId") 353 | """ 354 | The ID of the vault where the item is saved 355 | """ 356 | fields: List[ItemField] 357 | """ 358 | The item's fields 359 | """ 360 | sections: List[ItemSection] 361 | """ 362 | The item's sections 363 | """ 364 | notes: str 365 | """ 366 | The notes of the item 367 | """ 368 | tags: List[str] 369 | """ 370 | The item's tags 371 | """ 372 | websites: List[Website] 373 | """ 374 | The websites used for autofilling for items of the Login and Password categories. 375 | """ 376 | version: int 377 | """ 378 | The item's version 379 | """ 380 | files: List[ItemFile] 381 | """ 382 | The item's file fields 383 | """ 384 | document: Optional[FileAttributes] = Field(default=None) 385 | """ 386 | The document file for the Document item category 387 | """ 388 | created_at: Annotated[ 389 | datetime, 390 | BeforeValidator(parse_rfc3339), 391 | PlainSerializer(serialize_datetime_data), 392 | ] = Field(alias="createdAt") 393 | """ 394 | The time the item was created at 395 | """ 396 | updated_at: Annotated[ 397 | datetime, 398 | BeforeValidator(parse_rfc3339), 399 | PlainSerializer(serialize_datetime_data), 400 | ] = Field(alias="updatedAt") 401 | """ 402 | The time the item was updated at 403 | """ 404 | 405 | 406 | class ItemCreateParams(BaseModel): 407 | model_config = ConfigDict(populate_by_name=True) 408 | 409 | category: ItemCategory 410 | """ 411 | The item's category 412 | """ 413 | vault_id: str = Field(alias="vaultId") 414 | """ 415 | The ID of the vault where the item is saved 416 | """ 417 | title: str 418 | """ 419 | The item's title 420 | """ 421 | fields: Optional[List[ItemField]] = Field(default=None) 422 | """ 423 | The item's fields 424 | """ 425 | sections: Optional[List[ItemSection]] = Field(default=None) 426 | """ 427 | The item's sections 428 | """ 429 | notes: Optional[str] = Field(default=None) 430 | """ 431 | The item's notes 432 | """ 433 | tags: Optional[List[str]] = Field(default=None) 434 | """ 435 | The item's tags 436 | """ 437 | websites: Optional[List[Website]] = Field(default=None) 438 | """ 439 | The websites used for autofilling for items of the Login and Password categories. 440 | """ 441 | files: Optional[List[FileCreateParams]] = Field(default=None) 442 | """ 443 | The item's files stored as fields 444 | """ 445 | document: Optional[DocumentCreateParams] = Field(default=None) 446 | """ 447 | The document file for the Document item type. Empty when the item isn't of Document type. 448 | """ 449 | 450 | 451 | class ItemState(str, Enum): 452 | """ 453 | Represents the state of an item in the SDK. 454 | """ 455 | 456 | ACTIVE = "active" 457 | """ 458 | The item is active 459 | """ 460 | ARCHIVED = "archived" 461 | """ 462 | The item is archived meaning it's hidden from regular view and stored in the archive. 463 | """ 464 | 465 | 466 | class ItemOverview(BaseModel): 467 | """ 468 | Represents a decrypted 1Password item overview. 469 | """ 470 | 471 | model_config = ConfigDict(populate_by_name=True) 472 | 473 | id: str 474 | """ 475 | The item's ID 476 | """ 477 | title: str 478 | """ 479 | The item's title 480 | """ 481 | category: ItemCategory 482 | """ 483 | The item's category 484 | """ 485 | vault_id: str = Field(alias="vaultId") 486 | """ 487 | The ID of the vault where the item is saved 488 | """ 489 | websites: List[Website] 490 | """ 491 | The websites used for autofilling for items of the Login and Password categories. 492 | """ 493 | tags: List[str] 494 | """ 495 | The item tags 496 | """ 497 | created_at: Annotated[ 498 | datetime, 499 | BeforeValidator(parse_rfc3339), 500 | PlainSerializer(serialize_datetime_data), 501 | ] = Field(alias="createdAt") 502 | """ 503 | The time the item was created at 504 | """ 505 | updated_at: Annotated[ 506 | datetime, 507 | BeforeValidator(parse_rfc3339), 508 | PlainSerializer(serialize_datetime_data), 509 | ] = Field(alias="updatedAt") 510 | """ 511 | The time the item was updated at 512 | """ 513 | state: ItemState 514 | """ 515 | Indicates the state of the item 516 | """ 517 | 518 | 519 | class ItemShareDuration(str, Enum): 520 | """ 521 | The valid duration options for sharing an item 522 | """ 523 | 524 | ONEHOUR = "OneHour" 525 | """ 526 | The share will expire in one hour 527 | """ 528 | ONEDAY = "OneDay" 529 | """ 530 | The share will expire in one day 531 | """ 532 | SEVENDAYS = "SevenDays" 533 | """ 534 | The share will expire in seven days 535 | """ 536 | FOURTEENDAYS = "FourteenDays" 537 | """ 538 | The share will expire in fourteen days 539 | """ 540 | THIRTYDAYS = "ThirtyDays" 541 | """ 542 | The share will expire in thirty days 543 | """ 544 | 545 | 546 | class AllowedType(str, Enum): 547 | """ 548 | The allowed types of item sharing, enforced by account policy 549 | """ 550 | 551 | AUTHENTICATED = "Authenticated" 552 | """ 553 | Allows creating share links with specific recipients 554 | """ 555 | PUBLIC = "Public" 556 | """ 557 | Allows creating public share links 558 | """ 559 | 560 | 561 | class AllowedRecipientType(str, Enum): 562 | """ 563 | The allowed recipient types of item sharing, enforced by account policy 564 | """ 565 | 566 | EMAIL = "Email" 567 | """ 568 | Recipients can be specified by email address 569 | """ 570 | DOMAIN = "Domain" 571 | """ 572 | Recipients can be specified by domain 573 | """ 574 | 575 | 576 | class ItemShareFiles(BaseModel): 577 | """ 578 | The file sharing policy 579 | """ 580 | 581 | model_config = ConfigDict(populate_by_name=True) 582 | 583 | allowed: bool 584 | """ 585 | Whether files can be included in item shares 586 | """ 587 | max_size: int = Field(alias="maxSize") 588 | """ 589 | The maximum encrypted size (in bytes) an included file can be 590 | """ 591 | allowed_types: Optional[List[AllowedType]] = Field( 592 | alias="allowedTypes", default=None 593 | ) 594 | """ 595 | The allowed types of item sharing - either "Authenticated" (share to specific users) or "Public" (share to anyone with a link) 596 | """ 597 | allowed_recipient_types: Optional[List[AllowedRecipientType]] = Field( 598 | alias="allowedRecipientTypes", default=None 599 | ) 600 | """ 601 | The allowed recipient types of item sharing - either "Email" or "Domain" 602 | """ 603 | max_expiry: Optional[ItemShareDuration] = Field(alias="maxExpiry", default=None) 604 | """ 605 | The maximum duration that an item can be shared for 606 | """ 607 | default_expiry: Optional[ItemShareDuration] = Field( 608 | alias="defaultExpiry", default=None 609 | ) 610 | """ 611 | The default duration that an item is shared for 612 | """ 613 | max_views: Optional[int] = Field(alias="maxViews", default=None) 614 | """ 615 | The maximum number of times an item can be viewed. A null value means unlimited views 616 | """ 617 | 618 | 619 | class ItemShareAccountPolicy(BaseModel): 620 | """ 621 | The account policy for sharing items, set by your account owner/admin 622 | This policy is enforced server-side when sharing items 623 | """ 624 | 625 | model_config = ConfigDict(populate_by_name=True) 626 | 627 | max_expiry: ItemShareDuration = Field(alias="maxExpiry") 628 | """ 629 | The maximum duration that an item can be shared for 630 | """ 631 | default_expiry: ItemShareDuration = Field(alias="defaultExpiry") 632 | """ 633 | The default duration that an item is shared for 634 | """ 635 | max_views: Optional[int] = Field(alias="maxViews", default=None) 636 | """ 637 | The maximum number of times an item can be viewed. A null value means unlimited views 638 | """ 639 | allowed_types: List[AllowedType] = Field(alias="allowedTypes") 640 | """ 641 | The allowed types of item sharing - either "Authenticated" (share to specific users) or "Public" (share to anyone with a link) 642 | """ 643 | allowed_recipient_types: List[AllowedRecipientType] = Field( 644 | alias="allowedRecipientTypes" 645 | ) 646 | """ 647 | The allowed recipient types of item sharing - either "Email" or "Domain" 648 | """ 649 | files: ItemShareFiles 650 | """ 651 | The file sharing policy 652 | """ 653 | 654 | 655 | class ValidRecipientEmailInner(BaseModel): 656 | """ 657 | Generated type representing the anonymous struct variant `Email` of the `ValidRecipient` Rust enum 658 | """ 659 | 660 | email: str 661 | 662 | 663 | class ValidRecipientDomainInner(BaseModel): 664 | """ 665 | Generated type representing the anonymous struct variant `Domain` of the `ValidRecipient` Rust enum 666 | """ 667 | 668 | domain: str 669 | 670 | 671 | class ValidRecipientTypes(str, Enum): 672 | EMAIL = "Email" 673 | DOMAIN = "Domain" 674 | 675 | 676 | class ValidRecipientEmail(BaseModel): 677 | """ 678 | This exact email address 679 | """ 680 | 681 | type: Literal[ValidRecipientTypes.EMAIL] = ValidRecipientTypes.EMAIL 682 | parameters: ValidRecipientEmailInner 683 | 684 | 685 | class ValidRecipientDomain(BaseModel): 686 | """ 687 | Anyone with an email address from the specified domain 688 | """ 689 | 690 | type: Literal[ValidRecipientTypes.DOMAIN] = ValidRecipientTypes.DOMAIN 691 | parameters: ValidRecipientDomainInner 692 | 693 | 694 | # The validated recipient of an item share 695 | ValidRecipient = Union[ValidRecipientEmail, ValidRecipientDomain] 696 | 697 | 698 | class ItemShareParams(BaseModel): 699 | """ 700 | The configuration options for sharing an item 701 | These must respect the account policy on item sharing 702 | """ 703 | 704 | model_config = ConfigDict(populate_by_name=True) 705 | 706 | recipients: Optional[List[ValidRecipient]] = Field(default=None) 707 | """ 708 | Emails or domains of the item share recipients. If not provided, everyone with the share link will have access 709 | """ 710 | expire_after: Optional[ItemShareDuration] = Field(alias="expireAfter", default=None) 711 | """ 712 | The duration of the share in seconds. If not provided, defaults to the account policy's default expiry 713 | """ 714 | one_time_only: bool = Field(alias="oneTimeOnly") 715 | """ 716 | Whether the item can only be viewed once per recipient 717 | """ 718 | 719 | 720 | class OtpFieldDetails(BaseModel): 721 | """ 722 | Additional attributes for OTP fields. 723 | """ 724 | 725 | model_config = ConfigDict(populate_by_name=True) 726 | 727 | code: Optional[str] = Field(default=None) 728 | """ 729 | The OTP code, if successfully computed 730 | """ 731 | error_message: Optional[str] = Field(alias="errorMessage", default=None) 732 | """ 733 | The error message, if the OTP code could not be computed 734 | """ 735 | 736 | 737 | class Response(BaseModel, Generic[T, E]): 738 | content: Optional[T] = Field(default=None) 739 | error: Optional[E] = Field(default=None) 740 | 741 | 742 | class ResolvedReference(BaseModel): 743 | model_config = ConfigDict(populate_by_name=True) 744 | 745 | secret: str 746 | item_id: str = Field(alias="itemId") 747 | vault_id: str = Field(alias="vaultId") 748 | 749 | 750 | class ResolveReferenceErrorTypes(str, Enum): 751 | PARSING = "parsing" 752 | FIELD_NOT_FOUND = "fieldNotFound" 753 | VAULT_NOT_FOUND = "vaultNotFound" 754 | TOO_MANY_VAULTS = "tooManyVaults" 755 | ITEM_NOT_FOUND = "itemNotFound" 756 | TOO_MANY_ITEMS = "tooManyItems" 757 | TOO_MANY_MATCHING_FIELDS = "tooManyMatchingFields" 758 | NO_MATCHING_SECTIONS = "noMatchingSections" 759 | INCOMPATIBLE_TOTP_QUERY_PARAMETER_FIELD = "incompatibleTOTPQueryParameterField" 760 | UNABLE_TO_GENERATE_TOTP_CODE = "unableToGenerateTotpCode" 761 | S_SH_KEY_METADATA_NOT_FOUND = "sSHKeyMetadataNotFound" 762 | UNSUPPORTED_FILE_FORMAT = "unsupportedFileFormat" 763 | INCOMPATIBLE_SSH_KEY_QUERY_PARAMETER_FIELD = "incompatibleSshKeyQueryParameterField" 764 | UNABLE_TO_PARSE_PRIVATE_KEY = "unableToParsePrivateKey" 765 | UNABLE_TO_FORMAT_PRIVATE_KEY_TO_OPEN_SSH = "unableToFormatPrivateKeyToOpenSsh" 766 | OTHER = "other" 767 | 768 | 769 | class ResolveReferenceErrorParsing(BaseModel): 770 | """ 771 | Error parsing the secret reference 772 | """ 773 | 774 | type: Literal[ResolveReferenceErrorTypes.PARSING] = ( 775 | ResolveReferenceErrorTypes.PARSING 776 | ) 777 | message: ErrorMessage 778 | 779 | 780 | class ResolveReferenceErrorFieldNotFound(BaseModel): 781 | """ 782 | The specified reference cannot be found within the item 783 | """ 784 | 785 | type: Literal[ResolveReferenceErrorTypes.FIELD_NOT_FOUND] = ( 786 | ResolveReferenceErrorTypes.FIELD_NOT_FOUND 787 | ) 788 | 789 | 790 | class ResolveReferenceErrorVaultNotFound(BaseModel): 791 | """ 792 | No vault matched the secret reference query 793 | """ 794 | 795 | type: Literal[ResolveReferenceErrorTypes.VAULT_NOT_FOUND] = ( 796 | ResolveReferenceErrorTypes.VAULT_NOT_FOUND 797 | ) 798 | 799 | 800 | class ResolveReferenceErrorTooManyVaults(BaseModel): 801 | """ 802 | More than one vault matched the secret reference query 803 | """ 804 | 805 | type: Literal[ResolveReferenceErrorTypes.TOO_MANY_VAULTS] = ( 806 | ResolveReferenceErrorTypes.TOO_MANY_VAULTS 807 | ) 808 | 809 | 810 | class ResolveReferenceErrorItemNotFound(BaseModel): 811 | """ 812 | No item matched the secret reference query 813 | """ 814 | 815 | type: Literal[ResolveReferenceErrorTypes.ITEM_NOT_FOUND] = ( 816 | ResolveReferenceErrorTypes.ITEM_NOT_FOUND 817 | ) 818 | 819 | 820 | class ResolveReferenceErrorTooManyItems(BaseModel): 821 | """ 822 | More than one item matched the secret reference query 823 | """ 824 | 825 | type: Literal[ResolveReferenceErrorTypes.TOO_MANY_ITEMS] = ( 826 | ResolveReferenceErrorTypes.TOO_MANY_ITEMS 827 | ) 828 | 829 | 830 | class ResolveReferenceErrorTooManyMatchingFields(BaseModel): 831 | """ 832 | More than one field matched the provided secret reference 833 | """ 834 | 835 | type: Literal[ResolveReferenceErrorTypes.TOO_MANY_MATCHING_FIELDS] = ( 836 | ResolveReferenceErrorTypes.TOO_MANY_MATCHING_FIELDS 837 | ) 838 | 839 | 840 | class ResolveReferenceErrorNoMatchingSections(BaseModel): 841 | """ 842 | No section found within the item for the provided identifier 843 | """ 844 | 845 | type: Literal[ResolveReferenceErrorTypes.NO_MATCHING_SECTIONS] = ( 846 | ResolveReferenceErrorTypes.NO_MATCHING_SECTIONS 847 | ) 848 | 849 | 850 | class ResolveReferenceErrorIncompatibleTOTPQueryParameterField(BaseModel): 851 | """ 852 | Incompatiable TOTP query parameters 853 | """ 854 | 855 | type: Literal[ 856 | ResolveReferenceErrorTypes.INCOMPATIBLE_TOTP_QUERY_PARAMETER_FIELD 857 | ] = ResolveReferenceErrorTypes.INCOMPATIBLE_TOTP_QUERY_PARAMETER_FIELD 858 | 859 | 860 | class ResolveReferenceErrorUnableToGenerateTotpCode(BaseModel): 861 | """ 862 | The totp was not able to be generated 863 | """ 864 | 865 | type: Literal[ResolveReferenceErrorTypes.UNABLE_TO_GENERATE_TOTP_CODE] = ( 866 | ResolveReferenceErrorTypes.UNABLE_TO_GENERATE_TOTP_CODE 867 | ) 868 | message: ErrorMessage 869 | 870 | 871 | class ResolveReferenceErrorSSHKeyMetadataNotFound(BaseModel): 872 | """ 873 | Couldn't find attributes specific to an SSH Key field 874 | """ 875 | 876 | type: Literal[ResolveReferenceErrorTypes.S_SH_KEY_METADATA_NOT_FOUND] = ( 877 | ResolveReferenceErrorTypes.S_SH_KEY_METADATA_NOT_FOUND 878 | ) 879 | 880 | 881 | class ResolveReferenceErrorUnsupportedFileFormat(BaseModel): 882 | """ 883 | Currently only support text files 884 | """ 885 | 886 | type: Literal[ResolveReferenceErrorTypes.UNSUPPORTED_FILE_FORMAT] = ( 887 | ResolveReferenceErrorTypes.UNSUPPORTED_FILE_FORMAT 888 | ) 889 | 890 | 891 | class ResolveReferenceErrorIncompatibleSshKeyQueryParameterField(BaseModel): 892 | """ 893 | Trying to convert a non-private key to a private key format 894 | """ 895 | 896 | type: Literal[ 897 | ResolveReferenceErrorTypes.INCOMPATIBLE_SSH_KEY_QUERY_PARAMETER_FIELD 898 | ] = ResolveReferenceErrorTypes.INCOMPATIBLE_SSH_KEY_QUERY_PARAMETER_FIELD 899 | 900 | 901 | class ResolveReferenceErrorUnableToParsePrivateKey(BaseModel): 902 | """ 903 | Unable to properly parse a private key string to convert to an internal Private Key type 904 | """ 905 | 906 | type: Literal[ResolveReferenceErrorTypes.UNABLE_TO_PARSE_PRIVATE_KEY] = ( 907 | ResolveReferenceErrorTypes.UNABLE_TO_PARSE_PRIVATE_KEY 908 | ) 909 | 910 | 911 | class ResolveReferenceErrorUnableToFormatPrivateKeyToOpenSsh(BaseModel): 912 | """ 913 | Unable to format a private key to OpenSSH format 914 | """ 915 | 916 | type: Literal[ 917 | ResolveReferenceErrorTypes.UNABLE_TO_FORMAT_PRIVATE_KEY_TO_OPEN_SSH 918 | ] = ResolveReferenceErrorTypes.UNABLE_TO_FORMAT_PRIVATE_KEY_TO_OPEN_SSH 919 | 920 | 921 | class ResolveReferenceErrorOther(BaseModel): 922 | """ 923 | Other type 924 | """ 925 | 926 | type: Literal[ResolveReferenceErrorTypes.OTHER] = ResolveReferenceErrorTypes.OTHER 927 | 928 | 929 | ResolveReferenceError = Union[ 930 | ResolveReferenceErrorParsing, 931 | ResolveReferenceErrorFieldNotFound, 932 | ResolveReferenceErrorVaultNotFound, 933 | ResolveReferenceErrorTooManyVaults, 934 | ResolveReferenceErrorItemNotFound, 935 | ResolveReferenceErrorTooManyItems, 936 | ResolveReferenceErrorTooManyMatchingFields, 937 | ResolveReferenceErrorNoMatchingSections, 938 | ResolveReferenceErrorIncompatibleTOTPQueryParameterField, 939 | ResolveReferenceErrorUnableToGenerateTotpCode, 940 | ResolveReferenceErrorSSHKeyMetadataNotFound, 941 | ResolveReferenceErrorUnsupportedFileFormat, 942 | ResolveReferenceErrorIncompatibleSshKeyQueryParameterField, 943 | ResolveReferenceErrorUnableToParsePrivateKey, 944 | ResolveReferenceErrorUnableToFormatPrivateKeyToOpenSsh, 945 | ResolveReferenceErrorOther, 946 | ] 947 | 948 | 949 | class ResolveAllResponse(BaseModel): 950 | model_config = ConfigDict(populate_by_name=True) 951 | 952 | individual_responses: Dict[ 953 | str, Response[ResolvedReference, ResolveReferenceError] 954 | ] = Field(alias="individualResponses") 955 | 956 | 957 | class SshKeyAttributes(BaseModel): 958 | model_config = ConfigDict(populate_by_name=True) 959 | 960 | public_key: str = Field(alias="publicKey") 961 | """ 962 | The public part of the SSH Key 963 | """ 964 | fingerprint: str 965 | """ 966 | The fingerprint of the SSH Key 967 | """ 968 | key_type: str = Field(alias="keyType") 969 | """ 970 | The key type ("Ed25519" or "RSA, {length}-bit") 971 | """ 972 | 973 | 974 | class VaultOverview(BaseModel): 975 | """ 976 | Represents a decrypted 1Password vault. 977 | """ 978 | 979 | id: str 980 | """ 981 | The vault's ID 982 | """ 983 | title: str 984 | """ 985 | The vault's title 986 | """ 987 | 988 | 989 | class ItemListFilterByStateInner(BaseModel): 990 | """ 991 | Generated type representing the anonymous struct variant `ByState` of the `ItemListFilter` Rust enum 992 | """ 993 | 994 | active: bool 995 | archived: bool 996 | 997 | 998 | class ItemListFilterTypes(str, Enum): 999 | BY_STATE = "ByState" 1000 | 1001 | 1002 | class ItemListFilterByState(BaseModel): 1003 | type: Literal[ItemListFilterTypes.BY_STATE] = ItemListFilterTypes.BY_STATE 1004 | content: ItemListFilterByStateInner 1005 | 1006 | 1007 | ItemListFilter = ItemListFilterByState 1008 | 1009 | 1010 | class PasswordRecipeMemorableInner(BaseModel): 1011 | """ 1012 | Generated type representing the anonymous struct variant `Memorable` of the `PasswordRecipe` Rust enum 1013 | """ 1014 | 1015 | model_config = ConfigDict(populate_by_name=True) 1016 | 1017 | separator_type: SeparatorType = Field(alias="separatorType") 1018 | """ 1019 | The type of separator between chunks. 1020 | """ 1021 | capitalize: bool 1022 | """ 1023 | Uppercase one randomly selected chunk. 1024 | """ 1025 | word_list_type: WordListType = Field(alias="wordListType") 1026 | """ 1027 | The type of word list used. 1028 | """ 1029 | word_count: int = Field(alias="wordCount") 1030 | """ 1031 | The number of "words" (words or syllables). 1032 | """ 1033 | 1034 | 1035 | class PasswordRecipePinInner(BaseModel): 1036 | """ 1037 | Generated type representing the anonymous struct variant `Pin` of the `PasswordRecipe` Rust enum 1038 | """ 1039 | 1040 | length: int 1041 | """ 1042 | Number of digits in the PIN. 1043 | """ 1044 | 1045 | 1046 | class PasswordRecipeRandomInner(BaseModel): 1047 | """ 1048 | Generated type representing the anonymous struct variant `Random` of the `PasswordRecipe` Rust enum 1049 | """ 1050 | 1051 | model_config = ConfigDict(populate_by_name=True) 1052 | 1053 | include_digits: bool = Field(alias="includeDigits") 1054 | """ 1055 | Include at least one digit in the password. 1056 | """ 1057 | include_symbols: bool = Field(alias="includeSymbols") 1058 | """ 1059 | Include at least one symbol in the password. 1060 | """ 1061 | length: int 1062 | """ 1063 | The length of the password. 1064 | """ 1065 | 1066 | 1067 | class PasswordRecipeTypes(str, Enum): 1068 | MEMORABLE = "Memorable" 1069 | PIN = "Pin" 1070 | RANDOM = "Random" 1071 | 1072 | 1073 | class PasswordRecipeMemorable(BaseModel): 1074 | type: Literal[PasswordRecipeTypes.MEMORABLE] = PasswordRecipeTypes.MEMORABLE 1075 | parameters: PasswordRecipeMemorableInner 1076 | 1077 | 1078 | class PasswordRecipePin(BaseModel): 1079 | type: Literal[PasswordRecipeTypes.PIN] = PasswordRecipeTypes.PIN 1080 | parameters: PasswordRecipePinInner 1081 | 1082 | 1083 | class PasswordRecipeRandom(BaseModel): 1084 | type: Literal[PasswordRecipeTypes.RANDOM] = PasswordRecipeTypes.RANDOM 1085 | parameters: PasswordRecipeRandomInner 1086 | 1087 | 1088 | PasswordRecipe = Union[PasswordRecipeMemorable, PasswordRecipePin, PasswordRecipeRandom] 1089 | 1090 | 1091 | class SeparatorType(str, Enum): 1092 | DIGITS = "digits" 1093 | """ 1094 | Randomly selected digits. 1095 | E.g, "`correct4horse0battery1staple`" 1096 | """ 1097 | DIGITSANDSYMBOLS = "digitsAndSymbols" 1098 | """ 1099 | Randomly selected digits and symbols. 1100 | This is useful to get word-based passwords to meet complexity requirements 1101 | E.g, "`correct4horse-battery1staple`" 1102 | """ 1103 | SPACES = "spaces" 1104 | """ 1105 | Spaces, like the original Diceware. 1106 | Great for mobile keyboards, not so great when people can overhear you type the password. 1107 | E.g, "`correct horse battery staple`" 1108 | """ 1109 | HYPHENS = "hyphens" 1110 | """ 1111 | Hyphens "`-`". 1112 | E.g, "`correct-horse-battery-staple`" 1113 | """ 1114 | UNDERSCORES = "underscores" 1115 | """ 1116 | "`_`". 1117 | E.g, "`correct_horse_battery_staple`" 1118 | """ 1119 | PERIODS = "periods" 1120 | """ 1121 | Period (full stop) "`.`". 1122 | E.g, "`correct.horse.battery.staple`" 1123 | """ 1124 | COMMAS = "commas" 1125 | """ 1126 | Comma "`,`". 1127 | E.g, "`correct,horse,battery,staple`" 1128 | """ 1129 | 1130 | 1131 | class WordListType(str, Enum): 1132 | FULLWORDS = "fullWords" 1133 | """ 1134 | Agile wordlist 1135 | """ 1136 | SYLLABLES = "syllables" 1137 | """ 1138 | English-like syllables 1139 | """ 1140 | THREELETTERS = "threeLetters" 1141 | """ 1142 | Three (random) letter "words" 1143 | """ 1144 | -------------------------------------------------------------------------------- /src/onepassword/vaults.py: -------------------------------------------------------------------------------- 1 | # Code generated by op-codegen - DO NO EDIT MANUALLY 2 | 3 | from .core import _invoke, _invoke_sync 4 | from typing import Optional, List 5 | from pydantic import TypeAdapter 6 | from .types import VaultOverview 7 | 8 | 9 | class Vaults: 10 | """ 11 | The Vaults API holds all the operations the SDK client can perform on 1Password vaults. 12 | """ 13 | 14 | def __init__(self, client_id): 15 | self.client_id = client_id 16 | 17 | async def list(self) -> List[VaultOverview]: 18 | """ 19 | List all vaults 20 | """ 21 | response = await _invoke( 22 | { 23 | "invocation": { 24 | "clientId": self.client_id, 25 | "parameters": {"name": "VaultsList", "parameters": {}}, 26 | } 27 | } 28 | ) 29 | 30 | response = TypeAdapter(List[VaultOverview]).validate_json(response) 31 | return response 32 | -------------------------------------------------------------------------------- /src/release/README.md: -------------------------------------------------------------------------------- 1 | ## How to Prepare a Release for the Python SDK 2 | 3 | Before running this script, the user must make sure that they have the write permissions to the Python SDK repository. 4 | 5 | Run this make command to install all dependencies required for the Python SDK release process. 6 | ``` 7 | release/install-dependencies 8 | ``` 9 | 10 | Step 1. Make any changes to the SDK as required on a feature branch or main branch. 11 | NOTE: If ran on a main branch, a release branch will be created. 12 | 13 | Step 2. Go to the root of the repo and run 14 | ``` 15 | make prep-release 16 | ``` 17 | Follow the scripts instructions and the release has now been prepped. 18 | 19 | Step 3. Ensure that the correct files have been updated - i.e. version/build files, release-notes has been updated. Check the latest commit on the branch to see your changes. 20 | 21 | Step 4. To build the wheels and source distribution for PyPi, run in the root of the repo: 22 | ``` 23 | make build-wheels 24 | ``` 25 | 26 | Step 5. Ensure your GITHUB_TOKEN environment variable is set as this will allow you to create the tags/release and push it. 27 | 28 | Step 6. Ensure you have the PyPi credentials to login when uploading the source and wheels to PyPi. 29 | 30 | Step 7. If everything looks good, at the root of the repo, run: 31 | ``` 32 | make release 33 | ``` 34 | Step 8. Congratulations, you have released the newest Python SDK! -------------------------------------------------------------------------------- /src/release/RELEASE-NOTES: -------------------------------------------------------------------------------- 1 | # 1Password Python SDK v0.3.0 2 | 3 | ## NEW 4 | 5 | - **Support for item states**: You can now fetch an item's state using the SDK. `ItemOverview` exposes one of two states: `Active` or `Archived`. 6 | - `Active`: An item located inside a vault. (Default) 7 | - `Archived`: An item that has been moved to the Archive. 1Password doesn't include archived items in search results or suggest them when you fill in apps and browsers. You can keep archived items as long as you'd like. 8 | - **Filtering listed items by state**: You can now filter the results of the item list function by item state. 9 | 10 | ## FIXED 11 | 12 | - **Deleting Archived Items:** The SDK now supports deleting items from the archive. 13 | 14 | ## ⚠️ BREAKING CHANGES ⚠️ 15 | This release contains breaking changes for two functions in the Python SDK. 16 | 17 | **Vault listing** 18 | 19 | * The function name has changed from `list_all` to `list`. To use this in your code, replace: 20 | ```python 21 | vaults = await client.vaults.list_all(vault_id) 22 | ``` 23 | with: 24 | ```python 25 | vaults = await client.vaults.list(vault_id) 26 | ``` 27 | 28 | * The return type of the vault listing function has changed from `SDKIterator[VaultOverview]` to `List[VaultOverview]`. To use this in your code, replace: 29 | 30 | ```python 31 | async for vault in vaults: 32 | # using vault overview 33 | ``` 34 | with: 35 | ```python 36 | for vault in vaults: 37 | # using vault overview 38 | ``` 39 | **Item listing** 40 | 41 | * The function name has changed from `ListAll` to `List`. To use this in your code, replace: 42 | ```python 43 | overviews = await client.items.list_all(vault_id) 44 | ``` 45 | with: 46 | ```python 47 | overviews = await client.items.list(vault_id, ItemListFilter( 48 | content=ItemListFilterByStateInner( 49 | active=True, 50 | archived=True, 51 | ) 52 | )) 53 | ``` 54 | 55 | * The return type of the item listing function has changed from `SDKIterator[ItemOverview]` to `List[ItemOverview]`. To use this in your code, replace: 56 | ```python 57 | async for overview in overviews: 58 | # using item overview 59 | ``` 60 | with: 61 | ```python 62 | for overview in overviews: 63 | # using item overview 64 | ``` 65 | 66 | This does not affect any code that's already deployed, and will not take effect in your codebase until you choose to update to version 0.3.0 or later of the 1Password Python SDK. 67 | -------------------------------------------------------------------------------- /src/release/scripts/build-wheels.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Helper script to build the required wheels for the Python SDK 4 | 5 | output_version_file="version.py" 6 | 7 | # The list of python verisons the SDKs release for 8 | python_versions=("$@") 9 | 10 | # Minimum glibc version we support 11 | glibc_version=2-32 12 | 13 | # These versions are being supported due to the SDKs supporting Python 3.9+ 14 | macOS_version_x86_64=10.9 15 | macOS_version_arm64=11.0 16 | 17 | # Extracts the current verison number for cleanup function 18 | current_version=$(awk -F "['\"]" '/SDK_VERSION =/{print $2}' "$output_version_file") 19 | 20 | # Function to execute upon exit 21 | cleanup() { 22 | echo "Performing cleanup tasks..." 23 | # Remove dist and egg-info and the potential release candidate if created 24 | rm -r dist src/*.egg-info/ onepassword_sdk-"${current_version}" 25 | exit 1 26 | } 27 | 28 | # Set the trap to call the cleanup function on exit 29 | trap cleanup SIGINT 30 | 31 | 32 | enforce_latest_code() { 33 | if [[ -n "$(git status --porcelain=v1)" ]]; then 34 | echo "ERROR: working directory is not clean." 35 | echo "Please stash your changes and try again." 36 | exit 1 37 | fi 38 | } 39 | 40 | build_wheels() { 41 | os_platform=$1 42 | machine_platform=$2 43 | 44 | export PYTHON_OS_PLATFORM=$os_platform 45 | export PYTHON_MACHINE_PLATFORM=$machine_platform 46 | 47 | case "$os_platform" in 48 | Darwin) 49 | macos_version= 50 | # Min MacOS version for Python 3.13+ is 10.13 51 | python_version=$(pyenv exec python3 --version 2>&1) 52 | 53 | if [[ "$machine_platform" == "x86_64" ]]; then 54 | if [[ "$python_version" == "Python 3.13"* ]]; then 55 | macos_version="10.13" 56 | else 57 | macos_version=$macOS_version_x86_64 58 | fi 59 | else 60 | macos_version=$macOS_version_arm64 61 | fi 62 | 63 | export _PYTHON_HOST_PLATFORM="macosx-${macos_version}-${PYTHON_MACHINE_PLATFORM}" 64 | ;; 65 | Linux) 66 | export _PYTHON_HOST_PLATFORM="manylinux-${glibc_version}-${PYTHON_MACHINE_PLATFORM}" 67 | ;; 68 | Windows) 69 | export _PYTHON_HOST_PLATFORM="win-${PYTHON_MACHINE_PLATFORM}" 70 | ;; 71 | *) 72 | echo "Unsupported OS: $os_platform" 73 | exit 1 74 | ;; 75 | esac 76 | 77 | pyenv exec python3 -m build --wheel 78 | rm -rf build 79 | } 80 | 81 | # Ensure that the current working directory is clean and building of wheels is made off of latest main 82 | enforce_latest_code 83 | 84 | # Acquire the wheels for different OS 85 | for python_version in "${python_versions[@]}"; do 86 | pyenv local $python_version 87 | build_wheels Darwin x86_64 88 | build_wheels Darwin arm64 89 | build_wheels Linux x86_64 90 | build_wheels Linux aarch64 91 | build_wheels Windows amd64 92 | done 93 | 94 | # Build Source as well incase wheels fails, pypi can install this as backup (standard practice) 95 | pyenv exec python3 -m build --sdist -------------------------------------------------------------------------------- /src/release/scripts/prep-release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Helper script to prepare a release for the Python SDK. 4 | 5 | output_version_file="version.py" 6 | output_build_file="src/onepassword/build_number.py" 7 | version_template_file="src/release/templates/version.tpl.py" 8 | build_number_template_file="src/release/templates/build_number.tpl.py" 9 | 10 | 11 | # Extracts the current build/version number for comparison and backup 12 | current_version=$(awk -F "['\"]" '/SDK_VERSION =/{print $2}' "$output_version_file") 13 | current_build=$(awk -F "['\"]" '/SDK_BUILD_NUMBER =/{print $2}' "$output_build_file") 14 | 15 | # Function to execute upon exit 16 | cleanup() { 17 | echo "Performing cleanup tasks..." 18 | # Revert changes to file if any 19 | sed -e "s/{{ version }}/$current_version/" "$version_template_file" > "$output_version_file" 20 | sed -e "s/{{ build }}/$current_build/" "$build_number_template_file" > "$output_build_file" 21 | exit 1 22 | } 23 | 24 | # Set the trap to call the cleanup function on exit 25 | trap cleanup SIGINT 26 | 27 | enforce_latest_code() { 28 | if [[ -n "$(git status --porcelain=v1)" ]]; then 29 | echo "ERROR: working directory is not clean." 30 | echo "Please stash your changes and try again." 31 | exit 1 32 | fi 33 | } 34 | 35 | # Function to validate the version number format x.y.z(-beta.w) 36 | update_and_validate_version() { 37 | while true; do 38 | # Prompt the user to input the version number 39 | read -p "Enter the version number (format: x.y.z(-beta.w)): " version 40 | 41 | # Validate the version number format 42 | if [[ "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-beta\.[0-9]+)?$ ]]; then 43 | if [[ "${current_version}" != "${version}" ]]; then 44 | # TODO: Check the less than case as well. 45 | echo "New version number is: ${version}" 46 | return 0 47 | else 48 | echo "Version hasn't changed." 49 | fi 50 | else 51 | echo "Invalid version number format: ${version}" 52 | echo "Please enter a version number in the 'x.y.z(-beta.w)' format." 53 | fi 54 | done 55 | } 56 | 57 | # Function to validate the build number format. 58 | # SEMVER Format: Mmmppbb - 7 Digits 59 | update_and_validate_build() { 60 | while true; do 61 | # Prompt the user to input the build number 62 | read -p "Enter the build number (format: Mmmppbb): " build 63 | 64 | # Validate the build number format 65 | if [[ "${build}" =~ ^[0-9]{7}$ ]]; then 66 | if (( 10#$current_build < 10#$build )); then 67 | # Write the valid build number to the file 68 | echo "New build number is: ${build}" 69 | return 0 70 | else 71 | echo "New build version should be higher than current build version." 72 | fi 73 | else 74 | echo "Invalid build number format: ${build}" 75 | echo "Please enter a build number in the 'Mmmppbb' format." 76 | fi 77 | done 78 | } 79 | 80 | # Ensure that the current working directory is clean 81 | enforce_latest_code 82 | 83 | # Update and validate the version number 84 | update_and_validate_version 85 | 86 | # Update and validate the build number 87 | update_and_validate_build 88 | 89 | # Update version & build number in version.py and build_number.py respectively 90 | sed -e "s/{{ version }}/$version/" "$version_template_file" > "$output_version_file" 91 | sed -e "s/{{ build }}/$build/" "$build_number_template_file" > "$output_build_file" 92 | 93 | 94 | printf "Press ENTER to edit the RELEASE-NOTES in your default editor...\n" 95 | read -r _ignore 96 | ${EDITOR:-nano} "src/release/RELEASE-NOTES" 97 | 98 | # Get Current Branch Name 99 | branch="$(git rev-parse --abbrev-ref HEAD)" 100 | 101 | # if on main, then stash changes and create RC branch 102 | if [[ "${branch}" = "main" ]]; then 103 | branch=rc/"${version}" 104 | git stash 105 | git fetch origin 106 | git checkout -b "${branch}" 107 | git stash apply 108 | fi 109 | 110 | # Add changes and commit/push to branch 111 | git add . 112 | git commit -S -m "Release v${version}" 113 | git push --set-upstream origin "${branch}" 114 | 115 | echo "Release has been prepared.. 116 | Make sure to double check version/build numbers in their appropriate files and 117 | changelog is correctly filled out. 118 | Once confirmed, run 'make release' to release the SDK!" 119 | 120 | -------------------------------------------------------------------------------- /src/release/scripts/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Helper script to release the Python SDK 4 | 5 | set -e 6 | 7 | # Read the contents of the files into variables 8 | version=$(awk -F "['\"]" '/SDK_VERSION =/{print $2}' "version.py") 9 | build=$(awk -F "['\"]" '/SDK_BUILD_NUMBER =/{print $2}' "src/onepassword/build_number.py") 10 | release_notes=$(< src/release/RELEASE-NOTES) 11 | 12 | # Check if Github CLI is installed 13 | if ! command -v gh &> /dev/null; then 14 | echo "gh is not installed";\ 15 | exit 1;\ 16 | fi 17 | 18 | # Ensure GITHUB_TOKEN env var is set 19 | if [ -z "${GITHUB_TOKEN}" ]; then 20 | echo "GITHUB_TOKEN environment variable is not set." 21 | exit 1 22 | fi 23 | 24 | git tag -a -s "v${version}" -m "${version}" 25 | 26 | # Push the tag to the branch 27 | git push origin tag "v${version}" 28 | 29 | gh release create "v${version}" --title "Release ${version}" --notes "${release_notes}" --repo github.com/1Password/onepassword-sdk-python 30 | 31 | # Release on PyPi 32 | python3 -m twine upload dist/* 33 | 34 | # Delete the dist folder after published 35 | rm -r dist src/*.egg-info 36 | 37 | -------------------------------------------------------------------------------- /src/release/templates/build_number.tpl.py: -------------------------------------------------------------------------------- 1 | SDK_BUILD_NUMBER = "{{ build }}" 2 | -------------------------------------------------------------------------------- /src/release/templates/version.tpl.py: -------------------------------------------------------------------------------- 1 | SDK_VERSION = "{{ version }}" 2 | -------------------------------------------------------------------------------- /version.py: -------------------------------------------------------------------------------- 1 | SDK_VERSION = "0.3.0" 2 | --------------------------------------------------------------------------------