├── .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 |
--------------------------------------------------------------------------------