clio-api-model-generator


Nameclio-api-model-generator JSON
Version 0.2.1 PyPI version JSON
download
home_pageNone
SummaryA utility to generate Clio API models from OpenAPI specifications for use with our Clio API Python Client.
upload_time2025-08-12 23:34:05
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseNone
keywords
VCS
bugtrack_url
requirements certifi charset-normalizer et_xmlfile idna mypy-extensions numpy openpyxl pandas python-dateutil pytz requests six typing-inspect typing_extensions tzdata urllib3 aiohttp pyyaml clio-api-model-generator
Travis-CI No Travis.
coveralls test coverage No coveralls.
            
## Usage

### Installation
```bash
pip install clio-api-model-generator
```
### Generating Models
To execute the generator and produce dataclass models, run:

```python
from clio_api_model_generator import clio_manage as manage_model_generator

manage_model_generator.generate_models(output_dir=models_dir, overwrite=False)
```

This script will initialize the models directory, download the OpenAPI spec, and generate all necessary dataclasses.

---

# Model Generator Generation Flow

**This document explains the setup process for handling the `models` directory, running generator scripts, and ensuring the correct paths are set dynamically.**

## Overview

The project handles the `models` directory in the following way:

1. **Checks for the existence of the `models` folder in the parent directory first (preferred).**
2. **If not found in the parent, it checks the project root directory.**
3. **If the folder exists and is not empty, it is backed up with a unique name (e.g., `models.backup`, `models.backup1`, etc.).**
4. **If no models folder is found, a new one is created in the preferred location (parent or project root).**
5. **Static files from the `static/` directory are copied into the models folder.**
6. **Environment variables are set to ensure the generator scripts use the correct folder paths.**
7. **Generator scripts are imported after paths are set.**

## Setup Process

## Execution Flow Summary

1. **Check parent directory for `models` folder first.**  
2. **Backup existing folder if not empty and create a new one.**  
3. **Copy static files into the models folder.**  
4. **Set environment variables with the correct paths.**  
5. **Import generator scripts that rely on the updated paths.**  

## Expected Outputs

### Case 1: Models folder found in the parent directory and backed up
```
Existing non-empty models directory found at: /home/user/models
Renamed "/home/user/models" to "/home/user/models.backup"
Created new "/home/user/models" directory.
Copied all files from "static/" to "/home/user/models".
Set environment variables for model paths: /home/user/models
```

### Case 2: No models folder found, creating a new one
```
No existing "models" directory found in parent or current directory.
Created new "/home/user/project/models" directory.
Copied all files from "static/" to "/home/user/project/models".
Set environment variables for model paths: /home/user/project/models
```

---

## Notes

- Ensure the `static/` directory exists and contains the required files before running the script.
- The parent directory is prioritized when determining the location of the `models` folder.
- Environment variables are crucial to dynamically set paths without modifying scripts manually.

---

# Dataclass Generator Scripts

This document provides an overview of the dataclass generator scripts, explaining their order of execution, flow, and parsing/extraction logic for each generator.

## Overview

The scripts process an OpenAPI specification to generate Python dataclasses for:

1. **Schemas** – Representing API response objects.
2. **Fields** – Handling OpenAPI field mappings.
3. **Query Parameters** – Handling API query parameters.
4. **Request Bodies** – Handling request payloads.
5. **Endpoints** – Registering API endpoints with generated models.

---

## Order of Execution

The generation process is orchestrated by `generate_models.py`, which follows this sequence:

1. **Initialization (folder setup and OpenAPI spec download)**
   - Ensures the `models` directory is ready.
   - Downloads the OpenAPI spec if not present.

2. **Schema Generation (`components.py`)**
   - Generates dataclasses from OpenAPI schemas.

3. **Field Extraction (`fields.py`)**
   - Extracts field definitions for models based on schema dependencies.

4. **Query Parameter Handling (`query.py`)**
   - Creates query dataclasses based on API query parameters.

5. **Request Body Handling (`request_body.py`)**
   - Processes and generates dataclasses for API request bodies.

6. **Endpoint Registry Creation (`endpoints.py`)**
   - Registers generated models to the API endpoint paths.

---

## Parsing and Extraction Logic

### 1. **Schema Generator (`components.py`)**

**Purpose:**  
Processes OpenAPI `components.schemas` to generate Python dataclasses.

**Key Logic:**  
- Reads the OpenAPI spec from `SPEC_FILE_PATH`.
- Iterates over schemas containing `_base` in the name.
- Recursively processes properties:
  - Required fields are listed first.
  - Optional fields are annotated with `Optional`.
- Handles nested objects by generating sub-dataclasses.

**Example Output:**

```python
@dataclass
class UserBase:
    id: int
    name: str
    email: Optional[str] = None
```

---

## Fields Generation (`fields.py`)

### **Purpose**

The `fields.py` script generates dataclasses that represent OpenAPI schema field definitions, including handling dependencies and nested resources.

### **Key Steps in Fields Generation**

1. **Reading OpenAPI Spec:**  
   - Reads schema definitions from `SPEC_FILE_PATH`.
   - Focuses on schemas without underscores (filters `_base` schemas).

2. **Processing Base Fields:**  
   - Base schema references are extracted from `allOf` definitions.
   - Fields are copied from the base schema into the generated dataclass.
   - Field names are sanitized to be valid Python identifiers.

3. **Handling Nested Resources:**  
   - Nested resources are identified within the schema.
   - If a nested reference is found, the corresponding nested resource dataclass is created and included in the parent class.
   - Nested fields are marked as `Optional` and are postponed if dependencies exist.

4. **Topological Sorting:**  
   - Ensures that dataclasses are generated in the correct dependency order.
   - Handles cyclic dependencies by raising errors.

5. **Mapping to Endpoints:**  
   - Generates a mapping of fields to API responses using list and show schemas.
   - Outputs a `endpoint_field_mapping.json` file for later use in endpoint definitions.

---

### **Example Workflow in `fields.py`**

#### Input OpenAPI Schema Example

```json
"Account": {
    "allOf": [
        { "$ref": "#/components/schemas/BaseEntity" },
        {
            "properties": {
                "name": { "type": "string" },
                "contacts": {
                    "$ref": "#/components/schemas/Contact"
                }
            }
        }
    ]
},
"Contact": {
    "properties": {
        "email": { "type": "string" },
        "phone": { "type": "string" }
    }
}
```

#### Output Generated Dataclass

```python
from dataclasses import dataclass
from typing import Optional
from models.schemas import *

@dataclass
class Account_Fields:
    id: Optional[int] = None
    created_at: Optional[str] = None
    name: Optional[str] = None
    contacts: Optional[Contact_Fields] = None
```

---

### **Handling Base Fields**

1. **Base Schema Reference Processing:**  
   - Extracted from the first `allOf` item.
   - Fields from the referenced schema (e.g., `BaseEntity`) are added.

2. **Base Fields Example:**  

   ```python
   @dataclass
   class BaseEntity:
       id: Optional[int] = None
       created_at: Optional[str] = None
   ```

---

### **Handling Nested Resources**

1. **Detecting Nested Fields:**  
   - Any field containing a reference (`$ref`) to another schema is treated as a nested resource.
   - These are appended with `_Fields` and added to the parent dataclass.

2. **Nested Field Example:**

   ```python
   @dataclass
   class Contact_Fields:
       email: Optional[str] = None
       phone: Optional[str] = None
   ```

3. **Embedding in Parent:**

   ```python
   @dataclass
   class Account_Fields:
       name: Optional[str] = None
       contacts: Optional[Contact_Fields] = None
   ```

---

### **Mapping to Endpoints**

The generator script processes schemas with `_list` and `_show` suffixes to establish relationships between fields and endpoint responses.

**Example Mapping in `endpoint_field_mapping.json`:**

```json
{
    "Account_list": "Account_Fields",
    "User_show": "User_Fields"
}
```

**Logic:**  
- The mapping associates the API response schemas to the generated field dataclasses.
- Helps the endpoint generator (`endpoints.py`) link responses to correct models.

---

### 3. **Query Generator (`query.py`)**

**Purpose:**  
Generates dataclasses for API query parameters.

**Key Logic:**  
- Reads query parameters from API operations.
- Transforms query parameter names to valid Python identifiers.
- Maps OpenAPI types to Python types.
- Writes required and optional fields separately.

**Example Output:**

```python
@dataclass
class AccountQuery:
    account_id: int
    status: Optional[str] = None
```

---

### 4. **Request Body Generator (`request_body.py`)**

**Purpose:**  
Processes request body parameters for API operations.

**Key Logic:**  
- Extracts the request body schema from `application/json` content.
- Handles nested object properties.
- Generates dataclasses with required and optional fields.

**Example Output:**

```python
@dataclass
class CreateAccountRequestBody:
    name: str
    email: str
    age: Optional[int] = None
```

---

### 5. **Endpoints Generator (`endpoints.py`)**

**Purpose:**  
Creates an `Endpoints` registry to store endpoint mappings.

**Key Logic:**  
- Iterates through API paths and methods.
- Stores metadata such as path, method, query, and request body models.
- Handles special download cases based on HTTP 303 responses.

**Example Output:**

```python
class Endpoints:
    registry = {
        'get_user': {
            'path': '/users/{user_id}',
            'method': 'GET',
            'query_model': UserQuery,
            'request_body_model': None
        }
    }
```

---

## Directory Structure

```
project-root/
│-- models/
│   ├── query.py
│   ├── request_body.py
│   ├── endpoints.py
│   ├── fields.py
│   ├── schemas.py
│-- static/
│   ├── __init__.py
│   ├── models_registry.py
│-- generate_models.py
│-- openapi.json
```
**Static files get copied to models directory automatically before generating the model dataclasses**
---

## Conclusion

This setup automates the creation of Python dataclasses from an OpenAPI spec, providing a structured way to handle API interactions and ensuring maintainability through a clear generation flow.


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "clio-api-model-generator",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": null,
    "author": null,
    "author_email": "Unigrated Partners <dev@unigratedpartners.com>",
    "download_url": "https://files.pythonhosted.org/packages/fe/86/4161367020de03289238687863eea3c21cf1cd538fa524ec2a07b803c204/clio_api_model_generator-0.2.1.tar.gz",
    "platform": null,
    "description": "\n## Usage\n\n### Installation\n```bash\npip install clio-api-model-generator\n```\n### Generating Models\nTo execute the generator and produce dataclass models, run:\n\n```python\nfrom clio_api_model_generator import clio_manage as manage_model_generator\n\nmanage_model_generator.generate_models(output_dir=models_dir, overwrite=False)\n```\n\nThis script will initialize the models directory, download the OpenAPI spec, and generate all necessary dataclasses.\n\n---\n\n# Model Generator Generation Flow\n\n**This document explains the setup process for handling the `models` directory, running generator scripts, and ensuring the correct paths are set dynamically.**\n\n## Overview\n\nThe project handles the `models` directory in the following way:\n\n1. **Checks for the existence of the `models` folder in the parent directory first (preferred).**\n2. **If not found in the parent, it checks the project root directory.**\n3. **If the folder exists and is not empty, it is backed up with a unique name (e.g., `models.backup`, `models.backup1`, etc.).**\n4. **If no models folder is found, a new one is created in the preferred location (parent or project root).**\n5. **Static files from the `static/` directory are copied into the models folder.**\n6. **Environment variables are set to ensure the generator scripts use the correct folder paths.**\n7. **Generator scripts are imported after paths are set.**\n\n## Setup Process\n\n## Execution Flow Summary\n\n1. **Check parent directory for `models` folder first.**  \n2. **Backup existing folder if not empty and create a new one.**  \n3. **Copy static files into the models folder.**  \n4. **Set environment variables with the correct paths.**  \n5. **Import generator scripts that rely on the updated paths.**  \n\n## Expected Outputs\n\n### Case 1: Models folder found in the parent directory and backed up\n```\nExisting non-empty models directory found at: /home/user/models\nRenamed \"/home/user/models\" to \"/home/user/models.backup\"\nCreated new \"/home/user/models\" directory.\nCopied all files from \"static/\" to \"/home/user/models\".\nSet environment variables for model paths: /home/user/models\n```\n\n### Case 2: No models folder found, creating a new one\n```\nNo existing \"models\" directory found in parent or current directory.\nCreated new \"/home/user/project/models\" directory.\nCopied all files from \"static/\" to \"/home/user/project/models\".\nSet environment variables for model paths: /home/user/project/models\n```\n\n---\n\n## Notes\n\n- Ensure the `static/` directory exists and contains the required files before running the script.\n- The parent directory is prioritized when determining the location of the `models` folder.\n- Environment variables are crucial to dynamically set paths without modifying scripts manually.\n\n---\n\n# Dataclass Generator Scripts\n\nThis document provides an overview of the dataclass generator scripts, explaining their order of execution, flow, and parsing/extraction logic for each generator.\n\n## Overview\n\nThe scripts process an OpenAPI specification to generate Python dataclasses for:\n\n1. **Schemas** \u2013 Representing API response objects.\n2. **Fields** \u2013 Handling OpenAPI field mappings.\n3. **Query Parameters** \u2013 Handling API query parameters.\n4. **Request Bodies** \u2013 Handling request payloads.\n5. **Endpoints** \u2013 Registering API endpoints with generated models.\n\n---\n\n## Order of Execution\n\nThe generation process is orchestrated by `generate_models.py`, which follows this sequence:\n\n1. **Initialization (folder setup and OpenAPI spec download)**\n   - Ensures the `models` directory is ready.\n   - Downloads the OpenAPI spec if not present.\n\n2. **Schema Generation (`components.py`)**\n   - Generates dataclasses from OpenAPI schemas.\n\n3. **Field Extraction (`fields.py`)**\n   - Extracts field definitions for models based on schema dependencies.\n\n4. **Query Parameter Handling (`query.py`)**\n   - Creates query dataclasses based on API query parameters.\n\n5. **Request Body Handling (`request_body.py`)**\n   - Processes and generates dataclasses for API request bodies.\n\n6. **Endpoint Registry Creation (`endpoints.py`)**\n   - Registers generated models to the API endpoint paths.\n\n---\n\n## Parsing and Extraction Logic\n\n### 1. **Schema Generator (`components.py`)**\n\n**Purpose:**  \nProcesses OpenAPI `components.schemas` to generate Python dataclasses.\n\n**Key Logic:**  \n- Reads the OpenAPI spec from `SPEC_FILE_PATH`.\n- Iterates over schemas containing `_base` in the name.\n- Recursively processes properties:\n  - Required fields are listed first.\n  - Optional fields are annotated with `Optional`.\n- Handles nested objects by generating sub-dataclasses.\n\n**Example Output:**\n\n```python\n@dataclass\nclass UserBase:\n    id: int\n    name: str\n    email: Optional[str] = None\n```\n\n---\n\n## Fields Generation (`fields.py`)\n\n### **Purpose**\n\nThe `fields.py` script generates dataclasses that represent OpenAPI schema field definitions, including handling dependencies and nested resources.\n\n### **Key Steps in Fields Generation**\n\n1. **Reading OpenAPI Spec:**  \n   - Reads schema definitions from `SPEC_FILE_PATH`.\n   - Focuses on schemas without underscores (filters `_base` schemas).\n\n2. **Processing Base Fields:**  \n   - Base schema references are extracted from `allOf` definitions.\n   - Fields are copied from the base schema into the generated dataclass.\n   - Field names are sanitized to be valid Python identifiers.\n\n3. **Handling Nested Resources:**  \n   - Nested resources are identified within the schema.\n   - If a nested reference is found, the corresponding nested resource dataclass is created and included in the parent class.\n   - Nested fields are marked as `Optional` and are postponed if dependencies exist.\n\n4. **Topological Sorting:**  \n   - Ensures that dataclasses are generated in the correct dependency order.\n   - Handles cyclic dependencies by raising errors.\n\n5. **Mapping to Endpoints:**  \n   - Generates a mapping of fields to API responses using list and show schemas.\n   - Outputs a `endpoint_field_mapping.json` file for later use in endpoint definitions.\n\n---\n\n### **Example Workflow in `fields.py`**\n\n#### Input OpenAPI Schema Example\n\n```json\n\"Account\": {\n    \"allOf\": [\n        { \"$ref\": \"#/components/schemas/BaseEntity\" },\n        {\n            \"properties\": {\n                \"name\": { \"type\": \"string\" },\n                \"contacts\": {\n                    \"$ref\": \"#/components/schemas/Contact\"\n                }\n            }\n        }\n    ]\n},\n\"Contact\": {\n    \"properties\": {\n        \"email\": { \"type\": \"string\" },\n        \"phone\": { \"type\": \"string\" }\n    }\n}\n```\n\n#### Output Generated Dataclass\n\n```python\nfrom dataclasses import dataclass\nfrom typing import Optional\nfrom models.schemas import *\n\n@dataclass\nclass Account_Fields:\n    id: Optional[int] = None\n    created_at: Optional[str] = None\n    name: Optional[str] = None\n    contacts: Optional[Contact_Fields] = None\n```\n\n---\n\n### **Handling Base Fields**\n\n1. **Base Schema Reference Processing:**  \n   - Extracted from the first `allOf` item.\n   - Fields from the referenced schema (e.g., `BaseEntity`) are added.\n\n2. **Base Fields Example:**  \n\n   ```python\n   @dataclass\n   class BaseEntity:\n       id: Optional[int] = None\n       created_at: Optional[str] = None\n   ```\n\n---\n\n### **Handling Nested Resources**\n\n1. **Detecting Nested Fields:**  \n   - Any field containing a reference (`$ref`) to another schema is treated as a nested resource.\n   - These are appended with `_Fields` and added to the parent dataclass.\n\n2. **Nested Field Example:**\n\n   ```python\n   @dataclass\n   class Contact_Fields:\n       email: Optional[str] = None\n       phone: Optional[str] = None\n   ```\n\n3. **Embedding in Parent:**\n\n   ```python\n   @dataclass\n   class Account_Fields:\n       name: Optional[str] = None\n       contacts: Optional[Contact_Fields] = None\n   ```\n\n---\n\n### **Mapping to Endpoints**\n\nThe generator script processes schemas with `_list` and `_show` suffixes to establish relationships between fields and endpoint responses.\n\n**Example Mapping in `endpoint_field_mapping.json`:**\n\n```json\n{\n    \"Account_list\": \"Account_Fields\",\n    \"User_show\": \"User_Fields\"\n}\n```\n\n**Logic:**  \n- The mapping associates the API response schemas to the generated field dataclasses.\n- Helps the endpoint generator (`endpoints.py`) link responses to correct models.\n\n---\n\n### 3. **Query Generator (`query.py`)**\n\n**Purpose:**  \nGenerates dataclasses for API query parameters.\n\n**Key Logic:**  \n- Reads query parameters from API operations.\n- Transforms query parameter names to valid Python identifiers.\n- Maps OpenAPI types to Python types.\n- Writes required and optional fields separately.\n\n**Example Output:**\n\n```python\n@dataclass\nclass AccountQuery:\n    account_id: int\n    status: Optional[str] = None\n```\n\n---\n\n### 4. **Request Body Generator (`request_body.py`)**\n\n**Purpose:**  \nProcesses request body parameters for API operations.\n\n**Key Logic:**  \n- Extracts the request body schema from `application/json` content.\n- Handles nested object properties.\n- Generates dataclasses with required and optional fields.\n\n**Example Output:**\n\n```python\n@dataclass\nclass CreateAccountRequestBody:\n    name: str\n    email: str\n    age: Optional[int] = None\n```\n\n---\n\n### 5. **Endpoints Generator (`endpoints.py`)**\n\n**Purpose:**  \nCreates an `Endpoints` registry to store endpoint mappings.\n\n**Key Logic:**  \n- Iterates through API paths and methods.\n- Stores metadata such as path, method, query, and request body models.\n- Handles special download cases based on HTTP 303 responses.\n\n**Example Output:**\n\n```python\nclass Endpoints:\n    registry = {\n        'get_user': {\n            'path': '/users/{user_id}',\n            'method': 'GET',\n            'query_model': UserQuery,\n            'request_body_model': None\n        }\n    }\n```\n\n---\n\n## Directory Structure\n\n```\nproject-root/\n\u2502-- models/\n\u2502   \u251c\u2500\u2500 query.py\n\u2502   \u251c\u2500\u2500 request_body.py\n\u2502   \u251c\u2500\u2500 endpoints.py\n\u2502   \u251c\u2500\u2500 fields.py\n\u2502   \u251c\u2500\u2500 schemas.py\n\u2502-- static/\n\u2502   \u251c\u2500\u2500 __init__.py\n\u2502   \u251c\u2500\u2500 models_registry.py\n\u2502-- generate_models.py\n\u2502-- openapi.json\n```\n**Static files get copied to models directory automatically before generating the model dataclasses**\n---\n\n## Conclusion\n\nThis setup automates the creation of Python dataclasses from an OpenAPI spec, providing a structured way to handle API interactions and ensuring maintainability through a clear generation flow.\n\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "A utility to generate Clio API models from OpenAPI specifications for use with our Clio API Python Client.",
    "version": "0.2.1",
    "project_urls": {
        "Clio API Python Client": "https://github.com/unigrated-solutions/clio-api-python-client",
        "Homepage": "https://github.com/unigrated-solutions/clio-api-model-generator"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "418dea8768ca04672871b2d9eb6b45bf5a363bc8c727d13953e2a6bd370bfc92",
                "md5": "c2be250f7b066526fd84680027e66d93",
                "sha256": "94bcbdcf5de92c6d071c4bf821def29af631c52beecd98bc583d206bd92f1e9f"
            },
            "downloads": -1,
            "filename": "clio_api_model_generator-0.2.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "c2be250f7b066526fd84680027e66d93",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 23271,
            "upload_time": "2025-08-12T23:34:04",
            "upload_time_iso_8601": "2025-08-12T23:34:04.044912Z",
            "url": "https://files.pythonhosted.org/packages/41/8d/ea8768ca04672871b2d9eb6b45bf5a363bc8c727d13953e2a6bd370bfc92/clio_api_model_generator-0.2.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "fe864161367020de03289238687863eea3c21cf1cd538fa524ec2a07b803c204",
                "md5": "187ff207ab9d626ba5f702dc9eb2e16e",
                "sha256": "27808ba64ccb4c1f5ad4c5ac5d3740818d31fb77eece8e486dc3d9f995abd022"
            },
            "downloads": -1,
            "filename": "clio_api_model_generator-0.2.1.tar.gz",
            "has_sig": false,
            "md5_digest": "187ff207ab9d626ba5f702dc9eb2e16e",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 21182,
            "upload_time": "2025-08-12T23:34:05",
            "upload_time_iso_8601": "2025-08-12T23:34:05.213461Z",
            "url": "https://files.pythonhosted.org/packages/fe/86/4161367020de03289238687863eea3c21cf1cd538fa524ec2a07b803c204/clio_api_model_generator-0.2.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-12 23:34:05",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "unigrated-solutions",
    "github_project": "clio-api-python-client",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "requirements": [
        {
            "name": "certifi",
            "specs": []
        },
        {
            "name": "charset-normalizer",
            "specs": []
        },
        {
            "name": "et_xmlfile",
            "specs": []
        },
        {
            "name": "idna",
            "specs": []
        },
        {
            "name": "mypy-extensions",
            "specs": []
        },
        {
            "name": "numpy",
            "specs": [
                [
                    "<",
                    "2.0"
                ]
            ]
        },
        {
            "name": "openpyxl",
            "specs": []
        },
        {
            "name": "pandas",
            "specs": []
        },
        {
            "name": "python-dateutil",
            "specs": []
        },
        {
            "name": "pytz",
            "specs": []
        },
        {
            "name": "requests",
            "specs": []
        },
        {
            "name": "six",
            "specs": []
        },
        {
            "name": "typing-inspect",
            "specs": []
        },
        {
            "name": "typing_extensions",
            "specs": []
        },
        {
            "name": "tzdata",
            "specs": []
        },
        {
            "name": "urllib3",
            "specs": []
        },
        {
            "name": "aiohttp",
            "specs": []
        },
        {
            "name": "pyyaml",
            "specs": []
        },
        {
            "name": "clio-api-model-generator",
            "specs": [
                [
                    "==",
                    "0.2.0"
                ]
            ]
        }
    ],
    "lcname": "clio-api-model-generator"
}
        
Elapsed time: 0.73100s