squiffy


Namesquiffy JSON
Version 0.1.3 PyPI version JSON
download
home_pageNone
SummarySquiffy is the minimal library that helps you build your CLI application before your boss notices
upload_time2024-08-27 22:29:24
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseMIT License Copyright (c) 2024 Gabriel Artemie Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
keywords cli application cli console console menu menu minimal terminal
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # squiffy - a light library for interactive CLI application

This is squiffy, a small, small - wonderfully small -  library for developing and deploying locally 
interactive CLI applications. 

It's intended for things that should be done quickly, with prime focuse on 
functionality and not on the infrastructure.

We aim to provide a bit of infrastructure for simple applications, that depend on user interaction
which triggers custom actions.

## Table of Contents

- [Installation](#installation)
- [Usage](#usage)
- [Examples](#examples)
- [Future Developments](#future-developments)
- [Contributing](#contributing)
- [License](#license)

## Installation

You can use *pip* to install squiffy into your environment.

```bash

pip install squiffy

```

Additionally we encourage usage of virtualenv to for development environments. 
we recommend to use pipenv for your development environment. 

Install pipenv using pip:

```bash
pip install pipenv
```

If restricted or there are problems with pip, you can use:

```bash
python -m pip install pipenv

```

Navigate in the directory you wish to install squiffy into and develop the app and
use *pipenv install* to install squiffy.

```bash
cd my_app
pipenv install squiffy

```
If you want to modify squiffy to suit your need, just clone the repo:

```bash
cd my_app
git init.
git clone https://github.com/Gygarte/squiffy.git

```

Create the environment and install de dependencies specified in the Pipfile using:

```bash
pipenv --python 3.12
pipenv install --dev

```

or:

```bash
python -m pipenv --python 3.12
python -m pipenv install --dev

```

Start using squiffy in your project. 


Note: Refer to the *pipenv* documentation here: https://pypi.org/project/pipenv/
## Usage

Squiffy uses four mandatory items to setup the CLI application:
1. **layout.json** the blueprint of your application's layout.
2. **LayoutFactory** the main class for app layout construction
3. **State** the main class for configuring an internal state - which contains whatever informations is needed in the app
4. **Application** the main class to be used

### Creating a layout.json

An example equals to 1000 words :D (please be advise that I purposfully let some keywords that 
do nothing yet! Squiffy is still in alpha and I'm still a noob)

```json
{
    "submenu": [
        {
            "title": "Main_Menu",
            "main":true,
            "logo_path": null,  // NOT IMPLEMENTED
            "header_msg": "This is the header for the main menu",
            "footer_msg": "Gabriel Artemie@2023",
            "return_to_previous": false,
            "return_to_main": false,
            "quit": true,
            "options": [
                {
                    "option": "SwitchToSubmenu2",
                    "include_help": true,  // NOT IMPLEMENTED
                    "help": "SwitchToSubmenu2 help",  // NOT IMPLEMENTED
                    "switch":"Second_Menu",
                    "action": null  // NOT IMPLEMENTED
                },
                {
                    "option": "Print_and_wait",
                    "include_help": true,  // NOT IMPLEMENTED
                    "help": "Print_and_wait help",  // NOT IMPLEMENTED
                    "switch":null,
                    "action": null  // NOT IMPLEMENTED
                },
                {
                    "option": "Trigger_an_error",
                    "include_help": true,  // NOT IMPLEMENTED
                    "help": "Trigger_an_error help",  // NOT IMPLEMENTED
                    "switch":null,
                    "action": null  // NOT IMPLEMENTED
                }
            ],
            "style":null // NOT IMPLEMENTED: to use a special style for each submenu
        },
        {
            "title": "Second_Menu",
            "main":false,
            "logo_path": null,  // NOT IMPLEMENTED
            "header_msg": "This is another header message for submenu2",
            "footer_msg": "Gabriel Artemie@2023",
            "return_to_previous": true,
            "return_to_main": true,
            "quit": true,
            "options": [
                {
                    "option": "Accept_an_input",
                    "include_help": true,  // NOT IMPLEMENTED
                    "help": "option1 help",  // NOT IMPLEMENTED
                    "switch":null,
                    "action": null  // NOT IMPLEMENTED
                },
                {
                    "option": "Print_the_state",
                    "include_help": true,  // NOT IMPLEMENTED
                    "help": "option2 help",  // NOT IMPLEMENTED
                    "switch":null,
                    "action": null  // NOT IMPLEMENTED
                }
            ],
            "style":null // NOT IMPLEMENTED: to use a special style for each submenu
        }
    ],
    "error_handling":{
        "name":"Error",
        "include":true,
        "log_path":null  // NOT IMPLEMENTED
    },
    "style_sheet_path":null, //NOT IMPLEMENTED: a path to a separate style sheet
    "default_style":{
        "dimensions":{
            "type":"auto",
            "width":null,
            "height":null
        },
        "padding":{
            "top":1,
            "right":2,
            "bottom":1,
            "left":2
        },
        "border":{
            "type":"light" // You can chouse from "light", "thick" and "double" border style
        }
    }
}

```
### The *LayoutFactory*

The layout is created by passing the path to the layout.json to the LayoutFactory constructor

```python
from squiffy import LayoutFactory

layout = LayoutFactory('my_app/layout.json') 

```

### The style

The style sheet is still a simple approach for a kinda' retro style type.
The inspiration for the style apperance was drawn from the **console-menu** project by @aergirhall
(many many thanks to you for the ideas and I hope you do not dissaprove that I've used them)

From the above **layout.json** you can guess what styling options are available in this version. 

Please see the [Future Developments](#future-developments) section bellow for details regarding the development of this feature.

### Setting a State

A **State** keeps data that are used by the callback functions. Using this you can pass informations 
to and from the callback funtions. 

The **State** is configured to be saved whenewer the app is quitting or an error is triggered. So, any information passed to the **State** should implement a saving mechanism. If there is a reason for which 
an object within the **State** should not be saved, pass it's name to the *save_except* attribute 
during class initialization, or implement a *save_except:bool* attribute within the object itself. 

No builtin objects are save excepted by default. So if you need to pass an builtin object through the 
**State**, make sure to pass it's name to the *save_except*.

Let's assume:
```python

from dataclass import datacalss

@dataclass
class MyConfig:

    # a bunch of attributes and configurations

    def save(self) -> None:
        # some saving logic

```

So the **State** will be defined as:

```python

state = State({"config":MyConfig()})

```

If you want to *MyConfig* from beeing saved, you can implement within the class the following:

```python

@dataclass
class MyConfig:
    save_except= True

```

or pass it's name to the *save_except*:

```python

state = State({'config':MyConfig()}, save_except= ['config'])
```

As we mentioned, a callback funtion can modify the **State** by returning an **OK** signal with *payload* containing a dictionary in the same format: **{name[str]:data[object]}**.

What happens when no *save* method exists and the object is not excepted? An **StateContentNotSavable** is raised during **State** initialization. 

### Setting the application

This is as simple as passing the **State** and **Layout** to the **Application** constructor and hit run.

```python

app = Application(layout=layout, state=state)

app.run()
```

### Setting callback functions

In order to do some stuff with all this we need some end function. The end functions should use the 
**State** and return a signal like *OK*, *Error*, *Abort*

Let's see an example:

```python
from squiffy import State
from squiffy.signals import OK, Error, Abort

def my_func(state:State) -> OK | Error | Abort:

    # do some stuff and return something

```

In order to save the results of the computation in the **State** you just pass a dictionary
with the *name_of_the_data* and the *actual_data_object* to the **OK.payload()**. 

If an error occured you can pass the following arguments to the **Error**: *origin*, *log_message*, *traceback*

```python

from squiffy import State
from squiffy.signals import OK, Error, Abort

def my_func(state:State) -> OK | Error | Abort:

    # all is good and the computation runned smoothly
    resulted_state = {"name_of_the_data":actual_data_object}

    return OK(pyload=resulted_state)

    # an error occured
    return Error()

    # you are fancy and use try-except and traceback.format_exc
    try:
        # some shady stuff
    except Exception:
        return Error(origin="here",log_message="Some shady error occured!", traceback=format_exc())

```

Finally your function is passed to the app instance as follows

```python

def main() -> None:

    app = Application(layout=layout, state=state)
    app.add(function=my_func, option="Option1", submenu="Submenu1") # tadaaaa

    app.run()

if __name__ == "__main__":
    main()
```

**That is all!** You can start building simple and beautifull stuff and show your work bestie the cool stuff you do because you do not have a real life :D. Cheers! 

## Examples

We enjoy having an example ready to be explored. just write the following and enjoy our small and not-so-creative example.

```bash

python -m squiffy.example.example

```

or if this fails, try to write the following in a *.py* file.

```bash

from squiffy import example

if __name__ == "__main__":
    example.main()

```

## Future Developments

1. Include the rendering and display of the logo based on a logo_path.
2. Include a keybinding for visualizing the *help* information for each option on the menu.
3. Include special stylesheet for each submenu (not sure I will follow this path tho!)
4. Include error logging into dedicated file.
5. Separate the style sheet from the layout sheet.
6. Refactoring: improvements of the architecture, code redability, commenting and more.
7. Include keybindings for common options like: *return_to_previous*, *return_to_main*, *quit* and others.
8. Write tests!  
9. Window auto-resizing.

## Contributing

We accept contributions, sugestions and any thought on how to improve squiffy or what edge cases should be treated (ofc and new features suggestions). 

## License

Please see the License section.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "squiffy",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "CLI application, cli, console, console menu, menu, minimal, terminal",
    "author": null,
    "author_email": "Gabriel Artemie <gabriel.artemie@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/ef/2d/d4d512a1bd346dd79e8499dd944c872502932255825c0ae0ea8d02686017/squiffy-0.1.3.tar.gz",
    "platform": null,
    "description": "# squiffy - a light library for interactive CLI application\n\nThis is squiffy, a small, small - wonderfully small -  library for developing and deploying locally \ninteractive CLI applications. \n\nIt's intended for things that should be done quickly, with prime focuse on \nfunctionality and not on the infrastructure.\n\nWe aim to provide a bit of infrastructure for simple applications, that depend on user interaction\nwhich triggers custom actions.\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Usage](#usage)\n- [Examples](#examples)\n- [Future Developments](#future-developments)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Installation\n\nYou can use *pip* to install squiffy into your environment.\n\n```bash\n\npip install squiffy\n\n```\n\nAdditionally we encourage usage of virtualenv to for development environments. \nwe recommend to use pipenv for your development environment. \n\nInstall pipenv using pip:\n\n```bash\npip install pipenv\n```\n\nIf restricted or there are problems with pip, you can use:\n\n```bash\npython -m pip install pipenv\n\n```\n\nNavigate in the directory you wish to install squiffy into and develop the app and\nuse *pipenv install* to install squiffy.\n\n```bash\ncd my_app\npipenv install squiffy\n\n```\nIf you want to modify squiffy to suit your need, just clone the repo:\n\n```bash\ncd my_app\ngit init.\ngit clone https://github.com/Gygarte/squiffy.git\n\n```\n\nCreate the environment and install de dependencies specified in the Pipfile using:\n\n```bash\npipenv --python 3.12\npipenv install --dev\n\n```\n\nor:\n\n```bash\npython -m pipenv --python 3.12\npython -m pipenv install --dev\n\n```\n\nStart using squiffy in your project. \n\n\nNote: Refer to the *pipenv* documentation here: https://pypi.org/project/pipenv/\n## Usage\n\nSquiffy uses four mandatory items to setup the CLI application:\n1. **layout.json** the blueprint of your application's layout.\n2. **LayoutFactory** the main class for app layout construction\n3. **State** the main class for configuring an internal state - which contains whatever informations is needed in the app\n4. **Application** the main class to be used\n\n### Creating a layout.json\n\nAn example equals to 1000 words :D (please be advise that I purposfully let some keywords that \ndo nothing yet! Squiffy is still in alpha and I'm still a noob)\n\n```json\n{\n    \"submenu\": [\n        {\n            \"title\": \"Main_Menu\",\n            \"main\":true,\n            \"logo_path\": null,  // NOT IMPLEMENTED\n            \"header_msg\": \"This is the header for the main menu\",\n            \"footer_msg\": \"Gabriel Artemie@2023\",\n            \"return_to_previous\": false,\n            \"return_to_main\": false,\n            \"quit\": true,\n            \"options\": [\n                {\n                    \"option\": \"SwitchToSubmenu2\",\n                    \"include_help\": true,  // NOT IMPLEMENTED\n                    \"help\": \"SwitchToSubmenu2 help\",  // NOT IMPLEMENTED\n                    \"switch\":\"Second_Menu\",\n                    \"action\": null  // NOT IMPLEMENTED\n                },\n                {\n                    \"option\": \"Print_and_wait\",\n                    \"include_help\": true,  // NOT IMPLEMENTED\n                    \"help\": \"Print_and_wait help\",  // NOT IMPLEMENTED\n                    \"switch\":null,\n                    \"action\": null  // NOT IMPLEMENTED\n                },\n                {\n                    \"option\": \"Trigger_an_error\",\n                    \"include_help\": true,  // NOT IMPLEMENTED\n                    \"help\": \"Trigger_an_error help\",  // NOT IMPLEMENTED\n                    \"switch\":null,\n                    \"action\": null  // NOT IMPLEMENTED\n                }\n            ],\n            \"style\":null // NOT IMPLEMENTED: to use a special style for each submenu\n        },\n        {\n            \"title\": \"Second_Menu\",\n            \"main\":false,\n            \"logo_path\": null,  // NOT IMPLEMENTED\n            \"header_msg\": \"This is another header message for submenu2\",\n            \"footer_msg\": \"Gabriel Artemie@2023\",\n            \"return_to_previous\": true,\n            \"return_to_main\": true,\n            \"quit\": true,\n            \"options\": [\n                {\n                    \"option\": \"Accept_an_input\",\n                    \"include_help\": true,  // NOT IMPLEMENTED\n                    \"help\": \"option1 help\",  // NOT IMPLEMENTED\n                    \"switch\":null,\n                    \"action\": null  // NOT IMPLEMENTED\n                },\n                {\n                    \"option\": \"Print_the_state\",\n                    \"include_help\": true,  // NOT IMPLEMENTED\n                    \"help\": \"option2 help\",  // NOT IMPLEMENTED\n                    \"switch\":null,\n                    \"action\": null  // NOT IMPLEMENTED\n                }\n            ],\n            \"style\":null // NOT IMPLEMENTED: to use a special style for each submenu\n        }\n    ],\n    \"error_handling\":{\n        \"name\":\"Error\",\n        \"include\":true,\n        \"log_path\":null  // NOT IMPLEMENTED\n    },\n    \"style_sheet_path\":null, //NOT IMPLEMENTED: a path to a separate style sheet\n    \"default_style\":{\n        \"dimensions\":{\n            \"type\":\"auto\",\n            \"width\":null,\n            \"height\":null\n        },\n        \"padding\":{\n            \"top\":1,\n            \"right\":2,\n            \"bottom\":1,\n            \"left\":2\n        },\n        \"border\":{\n            \"type\":\"light\" // You can chouse from \"light\", \"thick\" and \"double\" border style\n        }\n    }\n}\n\n```\n### The *LayoutFactory*\n\nThe layout is created by passing the path to the layout.json to the LayoutFactory constructor\n\n```python\nfrom squiffy import LayoutFactory\n\nlayout = LayoutFactory('my_app/layout.json') \n\n```\n\n### The style\n\nThe style sheet is still a simple approach for a kinda' retro style type.\nThe inspiration for the style apperance was drawn from the **console-menu** project by @aergirhall\n(many many thanks to you for the ideas and I hope you do not dissaprove that I've used them)\n\nFrom the above **layout.json** you can guess what styling options are available in this version. \n\nPlease see the [Future Developments](#future-developments) section bellow for details regarding the development of this feature.\n\n### Setting a State\n\nA **State** keeps data that are used by the callback functions. Using this you can pass informations \nto and from the callback funtions. \n\nThe **State** is configured to be saved whenewer the app is quitting or an error is triggered. So, any information passed to the **State** should implement a saving mechanism. If there is a reason for which \nan object within the **State** should not be saved, pass it's name to the *save_except* attribute \nduring class initialization, or implement a *save_except:bool* attribute within the object itself. \n\nNo builtin objects are save excepted by default. So if you need to pass an builtin object through the \n**State**, make sure to pass it's name to the *save_except*.\n\nLet's assume:\n```python\n\nfrom dataclass import datacalss\n\n@dataclass\nclass MyConfig:\n\n    # a bunch of attributes and configurations\n\n    def save(self) -> None:\n        # some saving logic\n\n```\n\nSo the **State** will be defined as:\n\n```python\n\nstate = State({\"config\":MyConfig()})\n\n```\n\nIf you want to *MyConfig* from beeing saved, you can implement within the class the following:\n\n```python\n\n@dataclass\nclass MyConfig:\n    save_except= True\n\n```\n\nor pass it's name to the *save_except*:\n\n```python\n\nstate = State({'config':MyConfig()}, save_except= ['config'])\n```\n\nAs we mentioned, a callback funtion can modify the **State** by returning an **OK** signal with *payload* containing a dictionary in the same format: **{name[str]:data[object]}**.\n\nWhat happens when no *save* method exists and the object is not excepted? An **StateContentNotSavable** is raised during **State** initialization. \n\n### Setting the application\n\nThis is as simple as passing the **State** and **Layout** to the **Application** constructor and hit run.\n\n```python\n\napp = Application(layout=layout, state=state)\n\napp.run()\n```\n\n### Setting callback functions\n\nIn order to do some stuff with all this we need some end function. The end functions should use the \n**State** and return a signal like *OK*, *Error*, *Abort*\n\nLet's see an example:\n\n```python\nfrom squiffy import State\nfrom squiffy.signals import OK, Error, Abort\n\ndef my_func(state:State) -> OK | Error | Abort:\n\n    # do some stuff and return something\n\n```\n\nIn order to save the results of the computation in the **State** you just pass a dictionary\nwith the *name_of_the_data* and the *actual_data_object* to the **OK.payload()**. \n\nIf an error occured you can pass the following arguments to the **Error**: *origin*, *log_message*, *traceback*\n\n```python\n\nfrom squiffy import State\nfrom squiffy.signals import OK, Error, Abort\n\ndef my_func(state:State) -> OK | Error | Abort:\n\n    # all is good and the computation runned smoothly\n    resulted_state = {\"name_of_the_data\":actual_data_object}\n\n    return OK(pyload=resulted_state)\n\n    # an error occured\n    return Error()\n\n    # you are fancy and use try-except and traceback.format_exc\n    try:\n        # some shady stuff\n    except Exception:\n        return Error(origin=\"here\",log_message=\"Some shady error occured!\", traceback=format_exc())\n\n```\n\nFinally your function is passed to the app instance as follows\n\n```python\n\ndef main() -> None:\n\n    app = Application(layout=layout, state=state)\n    app.add(function=my_func, option=\"Option1\", submenu=\"Submenu1\") # tadaaaa\n\n    app.run()\n\nif __name__ == \"__main__\":\n    main()\n```\n\n**That is all!** You can start building simple and beautifull stuff and show your work bestie the cool stuff you do because you do not have a real life :D. Cheers! \n\n## Examples\n\nWe enjoy having an example ready to be explored. just write the following and enjoy our small and not-so-creative example.\n\n```bash\n\npython -m squiffy.example.example\n\n```\n\nor if this fails, try to write the following in a *.py* file.\n\n```bash\n\nfrom squiffy import example\n\nif __name__ == \"__main__\":\n    example.main()\n\n```\n\n## Future Developments\n\n1. Include the rendering and display of the logo based on a logo_path.\n2. Include a keybinding for visualizing the *help* information for each option on the menu.\n3. Include special stylesheet for each submenu (not sure I will follow this path tho!)\n4. Include error logging into dedicated file.\n5. Separate the style sheet from the layout sheet.\n6. Refactoring: improvements of the architecture, code redability, commenting and more.\n7. Include keybindings for common options like: *return_to_previous*, *return_to_main*, *quit* and others.\n8. Write tests!  \n9. Window auto-resizing.\n\n## Contributing\n\nWe accept contributions, sugestions and any thought on how to improve squiffy or what edge cases should be treated (ofc and new features suggestions). \n\n## License\n\nPlease see the License section.\n",
    "bugtrack_url": null,
    "license": "MIT License  Copyright (c) 2024 Gabriel Artemie  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
    "summary": "Squiffy is the minimal library that helps you build your CLI application before your boss notices",
    "version": "0.1.3",
    "project_urls": {
        "Changelog": "https://github.com/Gygarte/squiffy/blob/main/CHANGELOG.rst",
        "Repository": "https://github.com/Gygarte/squiffy.git"
    },
    "split_keywords": [
        "cli application",
        " cli",
        " console",
        " console menu",
        " menu",
        " minimal",
        " terminal"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "2acc257ea55b7a92590eb2364d573f7c8c2e736b27d544639bc1ef70047e6ff2",
                "md5": "2ca0c1233ccbc637607849323834ba80",
                "sha256": "e39d329fb1ccc1ae0d6070565ecaf240f8feb0af7883fee114024bd3197ebf14"
            },
            "downloads": -1,
            "filename": "squiffy-0.1.3-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "2ca0c1233ccbc637607849323834ba80",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 30689,
            "upload_time": "2024-08-27T22:29:21",
            "upload_time_iso_8601": "2024-08-27T22:29:21.934384Z",
            "url": "https://files.pythonhosted.org/packages/2a/cc/257ea55b7a92590eb2364d573f7c8c2e736b27d544639bc1ef70047e6ff2/squiffy-0.1.3-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "ef2dd4d512a1bd346dd79e8499dd944c872502932255825c0ae0ea8d02686017",
                "md5": "7f4040c0ce423da4280ee5c1c818915f",
                "sha256": "f5c64d3373b36ac3b975dc163717a1854ddf09f38a33b25ace741908418218b4"
            },
            "downloads": -1,
            "filename": "squiffy-0.1.3.tar.gz",
            "has_sig": false,
            "md5_digest": "7f4040c0ce423da4280ee5c1c818915f",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 26338,
            "upload_time": "2024-08-27T22:29:24",
            "upload_time_iso_8601": "2024-08-27T22:29:24.669277Z",
            "url": "https://files.pythonhosted.org/packages/ef/2d/d4d512a1bd346dd79e8499dd944c872502932255825c0ae0ea8d02686017/squiffy-0.1.3.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-08-27 22:29:24",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "Gygarte",
    "github_project": "squiffy",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "requirements": [],
    "lcname": "squiffy"
}
        
Elapsed time: 0.89331s