# PYONEPASSWORD




## Description
A Python API to sign into and query a 1Password account using the `op` command.
## Requirements
- Python >= 3.9
- 1Password command-line tool >= 2.26.0
- Versions >= 2.21.0, < 2.26.0 supported but deprecated
- Versions < 2.21.0 are unsupported and an exception will be raised
- See [1Password Developer Documentation](https://developer.1password.com/docs/cli)
- Internet connectivity to 1Password.com
- The `op` command queries your online account, not your local vault
> Notes:
> - Generally `pyonepassword` will support up to 5 patch versions, including the current. E.g., 2.{24-28}.0. Five additional patch versions, e.g., 2.{19-23}.0, will be considered deprecated.
> - This version of `pyonepassword` does not support deprecated `op` 1.x versions. Support for those versions is still available, albeit with minimal maintanence. See [pyonepassword-legacy](https://github.com/zcutlip/pyonepassword-legacy) for more information.
## Installation
```shell
python3 -m pip install pyonepassword
```
## Overview
`pyonepassword` essentially has two parts:
1. Convenience Python classes for the various objects that the `op` command returns
2. A full-fledged API for querying a 1Password account
If you already have a workflow to drive the `op` command, handle authentication, and so forth, but would benefit from an API that can ingest `op`'s JSON and give you Python objects, you're in luck, number one might be just what you need.
On the other hand, if you're using `op` manually (maybe along side `jq`), or in shell scripts (or maybe not at all), and you'd like a full-service Python API rather than console commands, number two does that.
We'll get into some examples below for both of these.
## Example Usage
### Object API
`pyonepassword` provides Python classes for many of the objects `op` returns, including:
- Several "item" types (login, password, secure note, etc)
- User
- User List (e.g., from 'op user list')
- Group
- Group List
- Vault
- Vault List
- Account
- Account List
All of these classes provide assorted convenience properties. For example `obj.created_at` returns a proper Python `datetime` object.
All of the object types are fundamentally dictionaries, so their data can be accessed as such, and they can be serialized back to JSON.
Also, all classes can be instantiated from either directly from a JSON string, or from an unserialized object.
Take the following Login item as an example:
```JSON
{
"id": "4smjvvepfbg3hencrmo7cozphe",
"title": "Example Login",
"version": 2,
"vault": {
"id": "yhdg6ovhkjcfhn3u25cp2bnl6e"
},
"category": "LOGIN",
"last_edited_by": "RAXCWKNRRNGL7I3KSZOH5ERLHI",
"created_at": "2021-06-29T18:42:03Z",
"updated_at": "2022-03-17T03:40:49Z",
"sections": [
{
"id": "linked items",
"label": "Related Items"
}
],
"fields": [
{
"id": "password",
"type": "CONCEALED",
"purpose": "PASSWORD",
"label": "password",
"value": "doth-parrot-hid-tussock-veldt",
"password_details": {
"strength": "FANTASTIC"
}
},
{
"id": "username",
"type": "STRING",
"purpose": "USERNAME",
"label": "username",
"value": "zcutlip"
},
{
"id": "notesPlain",
"type": "STRING",
"purpose": "NOTES",
"label": "notesPlain"
}
],
"urls": [
{
"href": "http://example2.website"
},
{
"primary": true,
"href": "https://example.website"
}
]
}
```
In just a line of Python, you can create an `OPLoginItem` object:
```Python
from pyonepassword.api.object_types import OPLoginItem
login_item = OPLoginItem(login_item_json)
print(login_item.username)
print(login_item.password)
print(login_item.primary_url.href)
# login_item is also a dictionary:
print(login_item["username"] == login_item.username)
```
### Example usage of the `OP` class
If you want to fully automate connecting to and querying a 1Password account, that's what the `OP` class is for. It handles authentication (except for initial sign-in). And provides methods that are congruent to many of the `op` CLI tool's subcommands, such as:
- `item_get()`
- `item_list()`
- `user_get()`
- `user_list()`...
... and so forth.
All of these methods return objects types as described above. Also, `item_get()` returns the appropriate object type for the item, such as `OPLoginItem` or `OPSecureNoteItem`, as long as `pyonepassword` has a class for the returned item type.
> *Note*: In some cases the `op` command may return items that don't conform to the expected structure. When this happens, the item dictionary will fail to validate, an exception will be raised. There is API for relaxing item validation, globally, on a per-class basis, or a per-item basis. See [item-validation.md](https://github.com/zcutlip/pyonepassword/blob/main/docs/item-validation.md) for more information.
### Sign-in and item retrieval
Below is an example demonstrating:
- Sign-in
- Specifying a default vault for queries
- Retrieving an item from 1Password by name or by UUID
- Overriding the default vault to retrieve a subsequent item from 1Password
```Python
import getpass
from pyonepassword import OP
from pyonepassword.api.exceptions import (
OPSigninException,
OPItemGetException,
OPNotFoundException,
OPConfigNotFoundException
)
# See examples/example-sign-in.py for more sign-in examples
def do_signin():
# Let's check If biometric is enabled
# If so, no need to provide a password
if OP.uses_biometric():
try:
# no need to provide any authentication parameters if biometric is enabled
op = OP()
except OPAuthenticationException:
print("Uh oh! Sign-in failed")
exit(-1)
else:
# prompt user for a password (or get it some other way)
my_password = getpass.getpass(prompt="1Password master password:\n")
# You may optionally provide an account shorthand if you used a custom one during initial sign-in
# shorthand = "arbitrary_account_shorthand"
# return OP(account_shorthand=shorthand, password=my_password)
# Or we'll try to look up account shorthand from your latest sign-in in op's config file
op = OP(password=my_password)
return op
def main():
op = do_signin()
item_password = op.item_get_password("Example Login")
# We can also look up the item by its UUID
# as well as retrieve from an alternate vault
item_password = op.item_get_password(
"ykhsbhhv2vf6hn2u4qwblfrmg4", vault="Private")
```
### Document retrieval
Below is an example demonstrating:
- Retrieving a document and its file name from 1Password, based on item name
- Retrieving a document & file name from 1Password, based on UUID
```Python
op = do_signin()
# File name and document title in 1Password are often different.
# so we get back the file name, and the bytes object representing the document
file_name, document_bytes = op.document_get("Example Login - 1Password Logo")
# we can also look up the document by UUID
file_name, document_bytes = op.document_get(
"bmxpvuthureo7e52uqmvqcr4dy")
open(file_name, "wb").write(document_bytes)
```
### Signing out of 1Password
Below is an example demonstrating:
- Signing in, then signing out of 1Password
- Signing out and also forgetting a 1Password account
> Note: Currently `pyonepassword`'s sign-out & forget support requires a signed-in session. It is not yet possible to forget an arbitrary account.
```Python
def main():
op = do_signin()
# do signout
op.signout()
try:
print(op.item_get_password("Example Login"))
except OPItemGetException:
# lookup fails since we signed out
pass
# now let's sign in again, then signout with forget=True
op = do_signin()
op.signout(forget=True)
try:
do_signin()
except OPSigninException:
# Sign-in fails since we erased the initial sign-in with forget=True
pass
```
### Getting Details for a User
```Python
op = OP(password=my_password)
# User's name:
user: OPUser = op.user_get("Firstname Lastname")
# or the user's UUID
user: OPUser = op.user_get(user_uuid)
```
### Getting Details for a Group
```Python
op = OP(password=my_password)
# Group name:
group: OPGroup = op.group_get("Team Members")
# or the group's UUID
group: OPGroup = op.group_get("yhdg6ovhkjcfhn3u25cp2bnl6e")
```
### Getting Details for a Vault
```Python
op = OP(password=my_password)
# Group name:
vault: OPVault = op.vault_get("Test Data")
# or the group's UUID
vault: OPVault = op.vault_get("yhdg6ovhkjcfhn3u25cp2bnl6e")
```
### Extending Item Types
If any of the item types (login, password, etc.) are missing or don't provide sufficient properties or methods, it's very easy to add new ones or extend existing ones.
Here's an example extending `OPLoginItem`.
```python
from pyonepassword import OP
from pyonepassword.api.decorators import op_register_item_type
from pyonepassword.api.object_types import OPLoginItem
@op_register_item_type
class OPEnhancedLoginItem(OPLoginItem):
@property
def custom_property(self):
return self["custom_field"]
op = OP()
enhanced_login = op.item_get("Example Login", vault="Test Data")
print(enhanced_login.custom_property)
```
### Item Deletion
```Python
from pyonepassword import OP # noqa: E402
from pyonepassword.api.exceptions import OPItemDeleteException # noqa: E402
def main():
op: OP()
try:
# op.item_delete() can take any identifier accepted by the 'op' command:
# Usage: op item delete [{ <itemName> | <itemID> | <shareLink> | - }] [flags]
deleted_uuid = op.item_delete("Example Login") # noqa: F841
# if desired inspect resulting UUID to ensure it's what was
# Expected
except OPItemDeleteException as ope:
# 'op' command can fail for a few reaons, including
# - item not found
# - duplicate item names
# Inspect the error message from the command
print(ope.err_output)
```
### Item Creation
For details on creating new items in a 1Password vault, see [item-creation.md](https://github.com/zcutlip/pyonepassword/blob/main/docs/item-creation.md)
Also see the examples in [examples/item_creation](https://github.com/zcutlip/pyonepassword/blob/main/examples/item_creation/)
### Item Editing
For details on editing existing items in a 1Password vault, see [item-editing.md](https://github.com/zcutlip/pyonepassword/blob/main/docs/item-editing.md)
Also see the examples in [examples/item_editing](https://github.com/zcutlip/pyonepassword/blob/main/examples/item_editing/)
### Document Editing
For details on editing existing document item file contents, see [document-editing.md](https://github.com/zcutlip/pyonepassword/blob/main/docs/document-editing.md)
See examples in [examples/document_editing](https://github.com/zcutlip/pyonepassword/blob/main/examples/document_editing.py)
### User Editing
User editing is supported via the `OP.user_edit()` method. It supports toggling travel mode on and off, as well as setting a new user name. Only one user at a time may be edited via this method.
See examples in [examples/user_editing](https://github.com/zcutlip/pyonepassword/blob/main/examples/user_editing.py)
### More Examples
Lots more examples are available in the `examples` directory
Raw data
{
"_id": null,
"home_page": "https://github.com/zcutlip/pyonepassword",
"name": "pyonepassword",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": null,
"author": "Zachary Cutlip",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/10/16/58966497259a003b24b6077f26441ab56f5e67084a4d30077cd716fc8cca/pyonepassword-5.0.1.tar.gz",
"platform": null,
"description": "# PYONEPASSWORD\n\n\n\n\n\n\n\n\n## Description\n\nA Python API to sign into and query a 1Password account using the `op` command.\n\n## Requirements\n\n- Python >= 3.9\n- 1Password command-line tool >= 2.26.0\n - Versions >= 2.21.0, < 2.26.0 supported but deprecated\n - Versions < 2.21.0 are unsupported and an exception will be raised\n - See [1Password Developer Documentation](https://developer.1password.com/docs/cli)\n- Internet connectivity to 1Password.com\n - The `op` command queries your online account, not your local vault\n\n> Notes:\n> - Generally `pyonepassword` will support up to 5 patch versions, including the current. E.g., 2.{24-28}.0. Five additional patch versions, e.g., 2.{19-23}.0, will be considered deprecated.\n> - This version of `pyonepassword` does not support deprecated `op` 1.x versions. Support for those versions is still available, albeit with minimal maintanence. See [pyonepassword-legacy](https://github.com/zcutlip/pyonepassword-legacy) for more information.\n\n## Installation\n\n```shell\npython3 -m pip install pyonepassword\n```\n\n## Overview\n\n`pyonepassword` essentially has two parts:\n\n1. Convenience Python classes for the various objects that the `op` command returns\n2. A full-fledged API for querying a 1Password account\n\nIf you already have a workflow to drive the `op` command, handle authentication, and so forth, but would benefit from an API that can ingest `op`'s JSON and give you Python objects, you're in luck, number one might be just what you need.\n\nOn the other hand, if you're using `op` manually (maybe along side `jq`), or in shell scripts (or maybe not at all), and you'd like a full-service Python API rather than console commands, number two does that.\n\nWe'll get into some examples below for both of these.\n\n\n\n## Example Usage\n\n### Object API\n\n`pyonepassword` provides Python classes for many of the objects `op` returns, including:\n\n- Several \"item\" types (login, password, secure note, etc)\n- User\n- User List (e.g., from 'op user list')\n- Group\n- Group List\n- Vault\n- Vault List\n- Account\n- Account List\n\nAll of these classes provide assorted convenience properties. For example `obj.created_at` returns a proper Python `datetime` object.\n\nAll of the object types are fundamentally dictionaries, so their data can be accessed as such, and they can be serialized back to JSON.\n\nAlso, all classes can be instantiated from either directly from a JSON string, or from an unserialized object.\n\nTake the following Login item as an example:\n\n```JSON\n{\n \"id\": \"4smjvvepfbg3hencrmo7cozphe\",\n \"title\": \"Example Login\",\n \"version\": 2,\n \"vault\": {\n \"id\": \"yhdg6ovhkjcfhn3u25cp2bnl6e\"\n },\n \"category\": \"LOGIN\",\n \"last_edited_by\": \"RAXCWKNRRNGL7I3KSZOH5ERLHI\",\n \"created_at\": \"2021-06-29T18:42:03Z\",\n \"updated_at\": \"2022-03-17T03:40:49Z\",\n \"sections\": [\n {\n \"id\": \"linked items\",\n \"label\": \"Related Items\"\n }\n ],\n \"fields\": [\n {\n \"id\": \"password\",\n \"type\": \"CONCEALED\",\n \"purpose\": \"PASSWORD\",\n \"label\": \"password\",\n \"value\": \"doth-parrot-hid-tussock-veldt\",\n \"password_details\": {\n \"strength\": \"FANTASTIC\"\n }\n },\n {\n \"id\": \"username\",\n \"type\": \"STRING\",\n \"purpose\": \"USERNAME\",\n \"label\": \"username\",\n \"value\": \"zcutlip\"\n },\n {\n \"id\": \"notesPlain\",\n \"type\": \"STRING\",\n \"purpose\": \"NOTES\",\n \"label\": \"notesPlain\"\n }\n ],\n \"urls\": [\n {\n \"href\": \"http://example2.website\"\n },\n {\n \"primary\": true,\n \"href\": \"https://example.website\"\n }\n ]\n}\n```\n\nIn just a line of Python, you can create an `OPLoginItem` object:\n\n```Python\n\nfrom pyonepassword.api.object_types import OPLoginItem\n\nlogin_item = OPLoginItem(login_item_json)\n\n\nprint(login_item.username)\nprint(login_item.password)\nprint(login_item.primary_url.href)\n\n# login_item is also a dictionary:\nprint(login_item[\"username\"] == login_item.username)\n```\n\n### Example usage of the `OP` class\n\nIf you want to fully automate connecting to and querying a 1Password account, that's what the `OP` class is for. It handles authentication (except for initial sign-in). And provides methods that are congruent to many of the `op` CLI tool's subcommands, such as:\n\n- `item_get()`\n- `item_list()`\n- `user_get()`\n- `user_list()`...\n\n... and so forth.\n\nAll of these methods return objects types as described above. Also, `item_get()` returns the appropriate object type for the item, such as `OPLoginItem` or `OPSecureNoteItem`, as long as `pyonepassword` has a class for the returned item type.\n\n> *Note*: In some cases the `op` command may return items that don't conform to the expected structure. When this happens, the item dictionary will fail to validate, an exception will be raised. There is API for relaxing item validation, globally, on a per-class basis, or a per-item basis. See [item-validation.md](https://github.com/zcutlip/pyonepassword/blob/main/docs/item-validation.md) for more information.\n\n### Sign-in and item retrieval\n\nBelow is an example demonstrating:\n\n- Sign-in\n- Specifying a default vault for queries\n- Retrieving an item from 1Password by name or by UUID\n- Overriding the default vault to retrieve a subsequent item from 1Password\n\n```Python\nimport getpass\n\nfrom pyonepassword import OP\nfrom pyonepassword.api.exceptions import (\n OPSigninException,\n OPItemGetException,\n OPNotFoundException,\n OPConfigNotFoundException\n)\n\n\n\n# See examples/example-sign-in.py for more sign-in examples\ndef do_signin():\n # Let's check If biometric is enabled\n # If so, no need to provide a password\n if OP.uses_biometric():\n try:\n # no need to provide any authentication parameters if biometric is enabled\n op = OP()\n except OPAuthenticationException:\n print(\"Uh oh! Sign-in failed\")\n exit(-1)\n else:\n # prompt user for a password (or get it some other way)\n my_password = getpass.getpass(prompt=\"1Password master password:\\n\")\n # You may optionally provide an account shorthand if you used a custom one during initial sign-in\n # shorthand = \"arbitrary_account_shorthand\"\n # return OP(account_shorthand=shorthand, password=my_password)\n # Or we'll try to look up account shorthand from your latest sign-in in op's config file\n op = OP(password=my_password)\n return op\n\n\ndef main():\n \top = do_signin()\n item_password = op.item_get_password(\"Example Login\")\n\n # We can also look up the item by its UUID\n # as well as retrieve from an alternate vault\n item_password = op.item_get_password(\n \"ykhsbhhv2vf6hn2u4qwblfrmg4\", vault=\"Private\")\n\n```\n\n### Document retrieval\n\nBelow is an example demonstrating:\n\n- Retrieving a document and its file name from 1Password, based on item name\n- Retrieving a document & file name from 1Password, based on UUID\n\n```Python\nop = do_signin()\n# File name and document title in 1Password are often different.\n# so we get back the file name, and the bytes object representing the document\nfile_name, document_bytes = op.document_get(\"Example Login - 1Password Logo\")\n\n# we can also look up the document by UUID\nfile_name, document_bytes = op.document_get(\n \"bmxpvuthureo7e52uqmvqcr4dy\")\nopen(file_name, \"wb\").write(document_bytes)\n```\n\n### Signing out of 1Password\n\nBelow is an example demonstrating:\n\n- Signing in, then signing out of 1Password\n- Signing out and also forgetting a 1Password account\n\n> Note: Currently `pyonepassword`'s sign-out & forget support requires a signed-in session. It is not yet possible to forget an arbitrary account.\n\n```Python\ndef main():\n\t op = do_signin()\n\n # do signout\n op.signout()\n\n try:\n\t\t print(op.item_get_password(\"Example Login\"))\n except OPItemGetException:\n \t# lookup fails since we signed out\n pass\n\n # now let's sign in again, then signout with forget=True\n op = do_signin()\n op.signout(forget=True)\n\n try:\n do_signin()\n except OPSigninException:\n\t\t\t\t# Sign-in fails since we erased the initial sign-in with forget=True\n\t\t\t\tpass\n```\n\n### Getting Details for a User\n\n```Python\nop = OP(password=my_password)\n\n# User's name:\nuser: OPUser = op.user_get(\"Firstname Lastname\")\n\n# or the user's UUID\nuser: OPUser = op.user_get(user_uuid)\n```\n\n### Getting Details for a Group\n\n```Python\nop = OP(password=my_password)\n\n# Group name:\ngroup: OPGroup = op.group_get(\"Team Members\")\n\n# or the group's UUID\ngroup: OPGroup = op.group_get(\"yhdg6ovhkjcfhn3u25cp2bnl6e\")\n```\n\n### Getting Details for a Vault\n\n```Python\nop = OP(password=my_password)\n\n# Group name:\nvault: OPVault = op.vault_get(\"Test Data\")\n\n# or the group's UUID\nvault: OPVault = op.vault_get(\"yhdg6ovhkjcfhn3u25cp2bnl6e\")\n```\n\n### Extending Item Types\n\nIf any of the item types (login, password, etc.) are missing or don't provide sufficient properties or methods, it's very easy to add new ones or extend existing ones.\n\nHere's an example extending `OPLoginItem`.\n\n```python\nfrom pyonepassword import OP\nfrom pyonepassword.api.decorators import op_register_item_type\nfrom pyonepassword.api.object_types import OPLoginItem\n\n@op_register_item_type\nclass OPEnhancedLoginItem(OPLoginItem):\n\n @property\n def custom_property(self):\n return self[\"custom_field\"]\n\n\nop = OP()\nenhanced_login = op.item_get(\"Example Login\", vault=\"Test Data\")\n\nprint(enhanced_login.custom_property)\n```\n\n### Item Deletion\n\n```Python\n\nfrom pyonepassword import OP # noqa: E402\nfrom pyonepassword.api.exceptions import OPItemDeleteException # noqa: E402\n\n\ndef main():\n op: OP()\n try:\n # op.item_delete() can take any identifier accepted by the 'op' command:\n # Usage: op item delete [{ <itemName> | <itemID> | <shareLink> | - }] [flags]\n deleted_uuid = op.item_delete(\"Example Login\") # noqa: F841\n # if desired inspect resulting UUID to ensure it's what was\n # Expected\n except OPItemDeleteException as ope:\n # 'op' command can fail for a few reaons, including\n # - item not found\n # - duplicate item names\n # Inspect the error message from the command\n print(ope.err_output)\n```\n\n\n### Item Creation\n\nFor details on creating new items in a 1Password vault, see [item-creation.md](https://github.com/zcutlip/pyonepassword/blob/main/docs/item-creation.md)\n\nAlso see the examples in [examples/item_creation](https://github.com/zcutlip/pyonepassword/blob/main/examples/item_creation/)\n\n\n### Item Editing\n\nFor details on editing existing items in a 1Password vault, see [item-editing.md](https://github.com/zcutlip/pyonepassword/blob/main/docs/item-editing.md)\n\nAlso see the examples in [examples/item_editing](https://github.com/zcutlip/pyonepassword/blob/main/examples/item_editing/)\n\n### Document Editing\n\nFor details on editing existing document item file contents, see [document-editing.md](https://github.com/zcutlip/pyonepassword/blob/main/docs/document-editing.md)\n\nSee examples in [examples/document_editing](https://github.com/zcutlip/pyonepassword/blob/main/examples/document_editing.py)\n\n### User Editing\n\nUser editing is supported via the `OP.user_edit()` method. It supports toggling travel mode on and off, as well as setting a new user name. Only one user at a time may be edited via this method.\n\nSee examples in [examples/user_editing](https://github.com/zcutlip/pyonepassword/blob/main/examples/user_editing.py)\n\n### More Examples\n\nLots more examples are available in the `examples` directory\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A python API to query a 1Password account using the 'op' command-line tool",
"version": "5.0.1",
"project_urls": {
"Homepage": "https://github.com/zcutlip/pyonepassword"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "dbd09f14dff81f7250a76e5b1eb5416d745e431fbcfc79084d29109b24ffeea3",
"md5": "72f813b549a7769e1c9aa1cf5bd8b214",
"sha256": "3629e0bbeda2586775f658b2843f1998256731f2da1fc05dd03f827f1bb1a871"
},
"downloads": -1,
"filename": "pyonepassword-5.0.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "72f813b549a7769e1c9aa1cf5bd8b214",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 115743,
"upload_time": "2024-08-06T03:16:02",
"upload_time_iso_8601": "2024-08-06T03:16:02.969474Z",
"url": "https://files.pythonhosted.org/packages/db/d0/9f14dff81f7250a76e5b1eb5416d745e431fbcfc79084d29109b24ffeea3/pyonepassword-5.0.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "101658966497259a003b24b6077f26441ab56f5e67084a4d30077cd716fc8cca",
"md5": "07b17110dd1054c50d7ba07521fe6491",
"sha256": "b3abc96295d763a95291b5113817f889654ba957430bdcdcf4547d137b2816f4"
},
"downloads": -1,
"filename": "pyonepassword-5.0.1.tar.gz",
"has_sig": false,
"md5_digest": "07b17110dd1054c50d7ba07521fe6491",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 95881,
"upload_time": "2024-08-06T03:16:05",
"upload_time_iso_8601": "2024-08-06T03:16:05.096018Z",
"url": "https://files.pythonhosted.org/packages/10/16/58966497259a003b24b6077f26441ab56f5e67084a4d30077cd716fc8cca/pyonepassword-5.0.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-08-06 03:16:05",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "zcutlip",
"github_project": "pyonepassword",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [],
"tox": true,
"lcname": "pyonepassword"
}