# Cabinet
Cabinet is a lightweight, flexible data organization tool that lets you manage your data with the simplicity of a JSON file or the power of MongoDB - your choice. It also lets you log messages to a file of your choice and send mail from the terminal.
## ✨ Features
- More easily access your data across multiple projects
- More easily log messages to the file of your choice
- Edit MongoDB as though it were a JSON file
- Log to a file/directory of your choice without having to configure `logger` each time
- Easily send mail from the terminal
## Breaking change in 2.0.0
- `mongodb_connection_string` replaces `mongodb_username` and `mongodb_password`.
## Installation and Setup
### CLI and Python Library (Recommended)
- Install [pipx](https://pypa.github.io/pipx/) if you don't have it already
- Install `cabinet`:
```bash
pipx install cabinet
cabinet --config
```
### CLI Only
```bash
curl -s https://api.github.com/repos/tylerjwoodfin/cabinet/releases/latest \
| grep "browser_download_url" \
| cut -d '"' -f 4 \
| xargs curl -L -o cabinet.pex
sudo mv cabinet.pex /usr/local/bin/cabinet
```
## Dependencies
Outside of the standard Python library, the following packages are included as part of `pipx install cabinet`:
- `pymongo`: Provides the MongoDB client and related errors.
- `prompt_toolkit`: Provides functionality for command-line interfaces.
## Structure
- Data is stored in `~/.cabinet/data.json` or MongoDB
- data from MongoDB is interacted with as if it were a JSON file
- cache is written when retrieving data.
- if cache is older than 1 hour, it is refreshed; otherwise, data is pulled from cache by default
- Logs are written to `~/.cabinet/log/LOG_DAILY_YYYY-MM-DD` by default
- You can change the path to something other than `~/.cabinet/log` as needed by setting/modifying `~/.config/cabinet/config.json` -> `path_dir_log`
## CLI usage
```markdown
Usage: cabinet [OPTIONS]
Options:
-h, --help show this help message and exit
--configure, -config Configure
--edit, -e Edit Cabinet as MongoDB as a JSON file
--edit-file EDIT_FILE, -ef EDIT_FILE
Edit a specific file
--force-cache-update Disable using the cache for MongoDB queries
--no-create (for -ef) Do not create file if it does not exist
--get GET [GET ...], -g GET [GET ...]
Get a property from MongoDB
--put PUT [PUT ...], -p PUT [PUT ...]
Put a property into MongoDB
--remove REMOVE [REMOVE ...], -rm REMOVE [REMOVE ...]
Remove a property from MongoDB
--get-file GET_FILE Get file
--export Exports MongoDB to ~/.cabinet/export
--strip (for --get-file) Whether to strip file content whitespace
--log LOG, -l LOG Log a message to the default location
--level LOG_LEVEL (for -l) Log level [debug, info, warn, error, critical]
--editor EDITOR (for --edit and --edit-file) Specify an editor to use
-v, --version Show version number and exit
Mail:
--mail Sends an email
--subject SUBJECT, -s SUBJECT
Email subject
--body BODY, -b BODY Email body
--to TO_ADDR, -t TO_ADDR
The "to" email address
```
## Configuration
- Configuration data is stored in `~/.config/cabinet/config.json`.
- Upon first launch, the tool will walk you through each option.
- `path_dir_log` is the directory where logs will be stored by default.
- `mongodb_enabled` is a boolean that determines whether MongoDB is used.
- `mongodb_db_name` is the name of the database you want to use by default.
- `mongodb_connection_string` is the connection string for MongoDB.
- `editor` is the default editor that will be used when editing files.
- You will be prompted to enter your MongoDB credentials (optional).
- If you choose not to use MongoDB, data will be stored in `~/.cabinet/data.json`.
- Follow these instructions to find your MongoDB connection string: [MongoDB Atlas](https://docs.atlas.mongodb.com/tutorial/connect-to-your-cluster/) or [MongoDB](https://docs.mongodb.com/manual/reference/connection-string/) (for local MongoDB, untested).
- You will be asked to configure your default editor from the list of available editors on
your system. If this step is skipped, or an error occurs, `nano` will be used.
You can change this with `cabinet --config` and modifying the `editor` attribute.
Your `config.json` should look something like this:
```json
{
"path_dir_log": "/path/to/your/log/directory",
"mongodb_db_name": "cabinet (or other name of your choice)",
"editor": "nvim",
"mongodb_enabled": true,
"mongodb_connection_string": "<your connection string>",
}
```
### edit_file() shortcuts
- see example below to enable something like
- `cabinet -ef shopping` from the terminal
- rather than `cabinet -ef "~/path/to/shopping_list.md"`
- or `cabinet.Cabinet().edit("shopping")`
- rather than `cabinet.Cabinet().edit("~/path/to/whatever.md")`
file:
```json
# example only; these commands will be unique to your setup
{
"path": {
"edit": {
"shopping": {
"value": "~/path/to/whatever.md",
},
"todo": {
"value": "~/path/to/whatever.md",
}
}
}
}
```
set from terminal:
```bash
cabinet -p edit shopping value "~/path/to/whatever.md"
cabinet -p edit todo value "~/path/to/whatever.md"
```
### mail
- It is NEVER a good idea to store your password openly either locally or in MongoDB; for this reason, I recommend a "throwaway" account that is only used for sending emails, such as a custom domain email.
- Gmail and most other mainstream email providers won't work with this; for support, search for sending mail from your email provider with `smtplib`.
- In Cabinet (`cabinet -e`), add the `email` object to make your settings file look like this example:
file:
```json
{
"email": {
"from": "throwaway@example.com",
"from_pw": "example",
"from_name": "Cabinet (or other name of your choice)",
"to": "destination@protonmail.com",
"smtp_server": "example.com",
"imap_server": "example.com",
"port": 123
}
}
```
set from terminal:
```bash
cabinet -p email from throwaway@example.com
cabinet -p email from_pw example
...
```
## Examples
### `put`
python:
```python
from cabinet import Cabinet
cab = Cabinet()
cab.put("employee", "Tyler", "salary", 7.25)
```
or terminal:
```bash
cabinet -p employee Tyler salary 7.25
```
results in this structure in MongoDB:
```json
{
"employee": {
"Tyler": {
"salary": 7.25 # or "7.25" if done from terminal
}
}
}
```
### `get`
python:
```python
from cabinet import Cabinet
cab = Cabinet()
print(cab.get("employee", "Tyler", "salary"))
# or cab.get("employee", "Tyler", "salary", is_print = True)
```
or terminal:
```bash
cabinet -g employee Tyler salary
```
- optional: `--force-cache-update` to force a cache update
results in:
```bash
7.25
```
### `remove`
python:
```python
from cabinet import Cabinet
cab = Cabinet()
cab.remove("employee", "Tyler", "salary")
```
or terminal:
```bash
cabinet -rm employee Tyler salary
```
results in this structure in MongoDB:
```json
{
"employee": {
"tyler": {}
}
}
```
### `edit`
terminal:
```bash
# opens file in the default editor (`cabinet --config` -> 'editor'), saves upon exit
cabinet -e
# or
cabinet --edit
# you can add an 'editor':
cabinet -e --editor=code
```
### `edit_file`
python:
```python
from cabinet import Cabinet
cab = Cabinet()
# if put("path", "edit", "shopping", "/path/to/shopping.md") has been called, this will edit the file assigned to that shortcut.
# opens file in the default editor (`cabinet --config` -> 'editor'), saves upon exit
cab.edit("shopping")
# or you can edit a file directly...
cab.edit("/path/to/shopping.md")
# you can pass an 'editor' to override the default:
cab.edit("/path/to/shopping.md", editor="nvim")
```
terminal:
```bash
# assumes path -> edit -> shopping -> path/to/shopping.md has been set
cabinet -ef shoppping
# or
cabinet -ef "/path/to/shopping.md"
# or
```
### `mail`
python:
```python
from cabinet import Mail
mail = Mail()
mail.send('Test Subject', 'Test Body')
```
terminal:
```bash
cabinet --mail --subject "Test Subject" --body "Test Body"
# or
cabinet --mail -s "Test Subject" -b "Test Body"
```
### `log`
python:
```python
from cabinet import Cabinet
cab = Cabinet()
# writes to a file named LOG_DAILY_YYYY-MM-DD in `~/.cabinet/log` inside a YYYY-MM-DD folder
# writes somewhere other than `~/.cabinet/log`, if `~/.config/cabinet/config.json` has `path_dir_log` set
cab.log("Connection timed out") # defaults to 'info' if no level is set
cab.log("This function hit a breakpoint", level="debug")
cab.log("Looks like the server is on fire", level="critical")
cab.log("This is fine", level="info")
# writes to a file named LOG_TEMPERATURE in the default log directory
cab.log("30", log_name="LOG_TEMPERATURE")
# writes to a file named LOG_TEMPERATURE in ~/weather
cab.log("30", log_name="LOG_TEMPERATURE", log_folder_path="~/weather")
# format
# 2021-12-29 19:29:27,896 — INFO — 30
```
terminal:
```bash
# defaults to 'info' if no level is set
cabinet -l "Connection timed out"
# -l and --log are interchangeable
cabinet --log "Connection timed out"
# change levels with --level
cabinet --log "Server is on fire" --level "critical"
```
### `logdb`
python:
```python
from cabinet import Cabinet
cab = Cabinet()
cab.logdb("Connection timed out") # logs default to a `logs` collection in MongoDB
cab.logdb("This function hit a breakpoint", level="debug", collection_name="debugging logs") # customize the collection name
cab.logdb("Temperature changed significantly", level="critical", db_name="weather") # customize the database name
cab.logdb("This is fine", level="info", cluster_name="myCluster") # customize the cluster name
```
terminal:
```bash
# defaults to 'info' if no level is set
cabinet -ldb "Connection timed out"
# -l and --log are interchangeable
cabinet --logdb "Connection timed out"
# change levels with --level
cabinet --logdb "Server is on fire" --level "critical"
```
## Disclaimers
- Although I've done quite a bit of testing, I can't guarantee everything that works on my machine will work on yours. Always back up your data to multiple places to avoid data loss.
- If you find any issues, please contact me... or get your hands dirty and raise a PR!
## Author
- Tyler Woodfin
- [GitHub](https://www.github.com/tylerjwoodfin)
- [LinkedIn](https://www.linkedin.com/in/tylerjwoodfin)
- [Website](http://tyler.cloud)
Raw data
{
"_id": null,
"home_page": "https://github.com/tylerjwoodfin/cabinet",
"name": "cabinet",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": null,
"author": "Tyler Woodfin",
"author_email": "feedback-cabinet@tyler.cloud",
"download_url": "https://files.pythonhosted.org/packages/85/b3/fcc6497f0719c71875a8804bd8494e16c19d44b4b45e84c30bc66edce73a/cabinet-1!2.0.0.tar.gz",
"platform": null,
"description": "# Cabinet\nCabinet is a lightweight, flexible data organization tool that lets you manage your data with the simplicity of a JSON file or the power of MongoDB - your choice. It also lets you log messages to a file of your choice and send mail from the terminal.\n\n## \u2728 Features\n\n- More easily access your data across multiple projects\n- More easily log messages to the file of your choice\n- Edit MongoDB as though it were a JSON file\n- Log to a file/directory of your choice without having to configure `logger` each time\n- Easily send mail from the terminal\n\n## Breaking change in 2.0.0\n- `mongodb_connection_string` replaces `mongodb_username` and `mongodb_password`.\n\n## Installation and Setup\n\n### CLI and Python Library (Recommended)\n\n- Install [pipx](https://pypa.github.io/pipx/) if you don't have it already\n\n- Install `cabinet`:\n```bash\npipx install cabinet\ncabinet --config\n```\n\n### CLI Only\n```bash\ncurl -s https://api.github.com/repos/tylerjwoodfin/cabinet/releases/latest \\\n| grep \"browser_download_url\" \\\n| cut -d '\"' -f 4 \\\n| xargs curl -L -o cabinet.pex\n\nsudo mv cabinet.pex /usr/local/bin/cabinet\n```\n\n## Dependencies\n\nOutside of the standard Python library, the following packages are included as part of `pipx install cabinet`:\n\n- `pymongo`: Provides the MongoDB client and related errors.\n- `prompt_toolkit`: Provides functionality for command-line interfaces.\n\n## Structure\n\n- Data is stored in `~/.cabinet/data.json` or MongoDB\n - data from MongoDB is interacted with as if it were a JSON file\n - cache is written when retrieving data.\n - if cache is older than 1 hour, it is refreshed; otherwise, data is pulled from cache by default\n- Logs are written to `~/.cabinet/log/LOG_DAILY_YYYY-MM-DD` by default\n - You can change the path to something other than `~/.cabinet/log` as needed by setting/modifying `~/.config/cabinet/config.json` -> `path_dir_log`\n\n## CLI usage\n```markdown\nUsage: cabinet [OPTIONS]\n\nOptions:\n -h, --help show this help message and exit\n --configure, -config Configure\n --edit, -e Edit Cabinet as MongoDB as a JSON file\n --edit-file EDIT_FILE, -ef EDIT_FILE\n Edit a specific file\n --force-cache-update Disable using the cache for MongoDB queries\n --no-create (for -ef) Do not create file if it does not exist\n --get GET [GET ...], -g GET [GET ...]\n Get a property from MongoDB\n --put PUT [PUT ...], -p PUT [PUT ...]\n Put a property into MongoDB\n --remove REMOVE [REMOVE ...], -rm REMOVE [REMOVE ...]\n Remove a property from MongoDB\n --get-file GET_FILE Get file\n --export Exports MongoDB to ~/.cabinet/export\n --strip (for --get-file) Whether to strip file content whitespace\n --log LOG, -l LOG Log a message to the default location\n --level LOG_LEVEL (for -l) Log level [debug, info, warn, error, critical]\n --editor EDITOR (for --edit and --edit-file) Specify an editor to use\n -v, --version Show version number and exit\n\nMail:\n --mail Sends an email\n --subject SUBJECT, -s SUBJECT\n Email subject\n --body BODY, -b BODY Email body\n --to TO_ADDR, -t TO_ADDR\n The \"to\" email address\n```\n\n## Configuration\n\n- Configuration data is stored in `~/.config/cabinet/config.json`.\n\n- Upon first launch, the tool will walk you through each option.\n - `path_dir_log` is the directory where logs will be stored by default.\n - `mongodb_enabled` is a boolean that determines whether MongoDB is used.\n - `mongodb_db_name` is the name of the database you want to use by default.\n - `mongodb_connection_string` is the connection string for MongoDB.\n - `editor` is the default editor that will be used when editing files.\n - You will be prompted to enter your MongoDB credentials (optional).\n - If you choose not to use MongoDB, data will be stored in `~/.cabinet/data.json`.\n\n- Follow these instructions to find your MongoDB connection string: [MongoDB Atlas](https://docs.atlas.mongodb.com/tutorial/connect-to-your-cluster/) or [MongoDB](https://docs.mongodb.com/manual/reference/connection-string/) (for local MongoDB, untested).\n\n- You will be asked to configure your default editor from the list of available editors on\n your system. If this step is skipped, or an error occurs, `nano` will be used.\n\n You can change this with `cabinet --config` and modifying the `editor` attribute.\n\nYour `config.json` should look something like this:\n```json\n{\n \"path_dir_log\": \"/path/to/your/log/directory\",\n \"mongodb_db_name\": \"cabinet (or other name of your choice)\",\n \"editor\": \"nvim\",\n \"mongodb_enabled\": true,\n \"mongodb_connection_string\": \"<your connection string>\",\n}\n```\n\n### edit_file() shortcuts\n- see example below to enable something like\n - `cabinet -ef shopping` from the terminal\n - rather than `cabinet -ef \"~/path/to/shopping_list.md\"`\n - or `cabinet.Cabinet().edit(\"shopping\")`\n - rather than `cabinet.Cabinet().edit(\"~/path/to/whatever.md\")`\n\nfile:\n```json\n# example only; these commands will be unique to your setup\n\n{\n \"path\": {\n \"edit\": {\n \"shopping\": {\n \"value\": \"~/path/to/whatever.md\",\n },\n \"todo\": {\n \"value\": \"~/path/to/whatever.md\",\n }\n }\n }\n}\n```\n\nset from terminal:\n```bash\ncabinet -p edit shopping value \"~/path/to/whatever.md\"\ncabinet -p edit todo value \"~/path/to/whatever.md\"\n```\n\n### mail\n\n- It is NEVER a good idea to store your password openly either locally or in MongoDB; for this reason, I recommend a \"throwaway\" account that is only used for sending emails, such as a custom domain email.\n- Gmail and most other mainstream email providers won't work with this; for support, search for sending mail from your email provider with `smtplib`.\n- In Cabinet (`cabinet -e`), add the `email` object to make your settings file look like this example:\n\nfile:\n```json\n{\n \"email\": {\n \"from\": \"throwaway@example.com\",\n \"from_pw\": \"example\",\n \"from_name\": \"Cabinet (or other name of your choice)\",\n \"to\": \"destination@protonmail.com\",\n \"smtp_server\": \"example.com\",\n \"imap_server\": \"example.com\",\n \"port\": 123\n }\n}\n```\n\nset from terminal:\n```bash\ncabinet -p email from throwaway@example.com\ncabinet -p email from_pw example\n...\n```\n\n## Examples\n\n### `put`\n\npython:\n```python\nfrom cabinet import Cabinet\n\ncab = Cabinet()\n\ncab.put(\"employee\", \"Tyler\", \"salary\", 7.25)\n```\n\nor terminal:\n```bash\ncabinet -p employee Tyler salary 7.25\n```\n\nresults in this structure in MongoDB:\n```json\n{\n \"employee\": {\n \"Tyler\": {\n \"salary\": 7.25 # or \"7.25\" if done from terminal\n }\n }\n}\n```\n\n### `get`\n\npython:\n```python\nfrom cabinet import Cabinet\n\ncab = Cabinet()\n\nprint(cab.get(\"employee\", \"Tyler\", \"salary\"))\n\n# or cab.get(\"employee\", \"Tyler\", \"salary\", is_print = True)\n```\n\nor terminal:\n```bash\ncabinet -g employee Tyler salary\n```\n- optional: `--force-cache-update` to force a cache update\n\nresults in:\n```bash\n7.25\n```\n\n### `remove`\n\npython:\n```python\nfrom cabinet import Cabinet\n\ncab = Cabinet()\n\ncab.remove(\"employee\", \"Tyler\", \"salary\")\n```\n\nor terminal:\n```bash\ncabinet -rm employee Tyler salary\n```\n\nresults in this structure in MongoDB:\n```json\n{\n \"employee\": {\n \"tyler\": {}\n }\n}\n```\n\n### `edit`\n\nterminal:\n```bash\n\n# opens file in the default editor (`cabinet --config` -> 'editor'), saves upon exit\ncabinet -e\n\n# or\n\ncabinet --edit\n\n# you can add an 'editor':\n\ncabinet -e --editor=code\n```\n\n### `edit_file`\n\npython:\n```python\nfrom cabinet import Cabinet\n\ncab = Cabinet()\n\n# if put(\"path\", \"edit\", \"shopping\", \"/path/to/shopping.md\") has been called, this will edit the file assigned to that shortcut.\n\n# opens file in the default editor (`cabinet --config` -> 'editor'), saves upon exit\ncab.edit(\"shopping\")\n\n# or you can edit a file directly...\ncab.edit(\"/path/to/shopping.md\")\n\n# you can pass an 'editor' to override the default:\ncab.edit(\"/path/to/shopping.md\", editor=\"nvim\")\n```\n\nterminal:\n```bash\n# assumes path -> edit -> shopping -> path/to/shopping.md has been set\ncabinet -ef shoppping\n\n# or\n\ncabinet -ef \"/path/to/shopping.md\"\n\n# or\n```\n\n### `mail`\n\npython:\n```python\n\nfrom cabinet import Mail\n\nmail = Mail()\n\nmail.send('Test Subject', 'Test Body')\n```\n\nterminal:\n```bash\ncabinet --mail --subject \"Test Subject\" --body \"Test Body\"\n\n# or\n\ncabinet --mail -s \"Test Subject\" -b \"Test Body\"\n```\n\n### `log`\n\npython:\n```python\nfrom cabinet import Cabinet\n\ncab = Cabinet()\n\n# writes to a file named LOG_DAILY_YYYY-MM-DD in `~/.cabinet/log` inside a YYYY-MM-DD folder\n# writes somewhere other than `~/.cabinet/log`, if `~/.config/cabinet/config.json` has `path_dir_log` set\n\ncab.log(\"Connection timed out\") # defaults to 'info' if no level is set\ncab.log(\"This function hit a breakpoint\", level=\"debug\")\ncab.log(\"Looks like the server is on fire\", level=\"critical\")\ncab.log(\"This is fine\", level=\"info\")\n\n# writes to a file named LOG_TEMPERATURE in the default log directory\ncab.log(\"30\", log_name=\"LOG_TEMPERATURE\")\n\n# writes to a file named LOG_TEMPERATURE in ~/weather\ncab.log(\"30\", log_name=\"LOG_TEMPERATURE\", log_folder_path=\"~/weather\")\n\n # format\n # 2021-12-29 19:29:27,896 \u2014 INFO \u2014 30\n\n```\n\nterminal:\n```bash\n# defaults to 'info' if no level is set\ncabinet -l \"Connection timed out\"\n\n# -l and --log are interchangeable\ncabinet --log \"Connection timed out\"\n\n# change levels with --level\ncabinet --log \"Server is on fire\" --level \"critical\"\n```\n\n### `logdb`\n\npython:\n```python\nfrom cabinet import Cabinet\ncab = Cabinet()\ncab.logdb(\"Connection timed out\") # logs default to a `logs` collection in MongoDB\ncab.logdb(\"This function hit a breakpoint\", level=\"debug\", collection_name=\"debugging logs\") # customize the collection name\ncab.logdb(\"Temperature changed significantly\", level=\"critical\", db_name=\"weather\") # customize the database name\ncab.logdb(\"This is fine\", level=\"info\", cluster_name=\"myCluster\") # customize the cluster name\n```\nterminal:\n```bash\n# defaults to 'info' if no level is set\ncabinet -ldb \"Connection timed out\"\n# -l and --log are interchangeable\ncabinet --logdb \"Connection timed out\"\n# change levels with --level\ncabinet --logdb \"Server is on fire\" --level \"critical\"\n```\n\n## Disclaimers\n\n- Although I've done quite a bit of testing, I can't guarantee everything that works on my machine will work on yours. Always back up your data to multiple places to avoid data loss.\n- If you find any issues, please contact me... or get your hands dirty and raise a PR!\n\n## Author\n\n- Tyler Woodfin\n - [GitHub](https://www.github.com/tylerjwoodfin)\n - [LinkedIn](https://www.linkedin.com/in/tylerjwoodfin)\n - [Website](http://tyler.cloud)\n",
"bugtrack_url": null,
"license": null,
"summary": "Easily manage data storage and logging across repos",
"version": "1!2.0.0",
"project_urls": {
"Homepage": "https://github.com/tylerjwoodfin/cabinet",
"bug tracker": "https://github.com/tylerjwoodfin/cabinet/issues"
},
"split_keywords": [],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "834c8bbda8b76111cf026958fc4592c9d0a30ed9c8126c073279daefbf6ab90d",
"md5": "859bf32b70b289929bf5198f174d59fa",
"sha256": "447f86fcc9c8d4d51dcf2c1bf08931fdf05855847562b8f5ef70add519a4d6c4"
},
"downloads": -1,
"filename": "cabinet-1!2.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "859bf32b70b289929bf5198f174d59fa",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 23213,
"upload_time": "2025-02-09T05:39:51",
"upload_time_iso_8601": "2025-02-09T05:39:51.212933Z",
"url": "https://files.pythonhosted.org/packages/83/4c/8bbda8b76111cf026958fc4592c9d0a30ed9c8126c073279daefbf6ab90d/cabinet-1!2.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "85b3fcc6497f0719c71875a8804bd8494e16c19d44b4b45e84c30bc66edce73a",
"md5": "3cbb86a0f8d828ac746203beea7c4048",
"sha256": "2c91e040971050977a283cb5be0a902015de8c7a526fcfb7776fc2e6bae75d4d"
},
"downloads": -1,
"filename": "cabinet-1!2.0.0.tar.gz",
"has_sig": false,
"md5_digest": "3cbb86a0f8d828ac746203beea7c4048",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 25231,
"upload_time": "2025-02-09T05:39:52",
"upload_time_iso_8601": "2025-02-09T05:39:52.467284Z",
"url": "https://files.pythonhosted.org/packages/85/b3/fcc6497f0719c71875a8804bd8494e16c19d44b4b45e84c30bc66edce73a/cabinet-1!2.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-02-09 05:39:52",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "tylerjwoodfin",
"github_project": "cabinet",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "cabinet"
}