seismos-package


Nameseismos-package JSON
Version 0.1.9 PyPI version JSON
download
home_pageNone
SummarySeismos internal Python package for structured logging and utilities.
upload_time2025-10-10 12:43:43
maintainerNone
docs_urlNone
authorSeismos
requires_python<3.13,>=3.8
licenseNone
keywords logging utilities internal-package
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Seismos Python Package

## Table of contents
- [Structured logging with structlog](#structured-logging-with-structlog)
  - [Basic configuration](#basic-configuration)
- [Middleware for automatic logging context](#middleware-for-automatic-logging-context)
  - [FastAPI quick start](#fastapi-quick-start)
- [ASGI Request Logging Middleware (FastAPI/Starlette)](#asgi-request-logging-middleware-fastapistarlette)
  - [What gets logged](#what-gets-logged)
  - [Notes](#notes)
- [Why use structured logging](#why-use-structured-logging)
- [Development of this project](#development-of-this-project)


## Structured logging with structlog
Structlog is a powerful logging library for structured, context-aware logging.
More details can be found in the [structlog](https://www.structlog.org/en/stable/).

### Basic configuration

instead of `logger = logging.getLogger(__name__)` it is `logger = structlog.get_logger(__name__)`

```python
    from seismos_package.logging import LoggingConfigurator
    from seismos_package.config import SeismosConfig
    import structlog

    config = SeismosConfig()

    LoggingConfigurator(
        service_name=config.APP_NAME,
        log_level='INFO',
        setup_logging_dict=True
    ).configure_structlog(
        formatter='plain_console',
        formatter_std_lib='plain_console'
    )

    logger = structlog.get_logger(__name__)
    logger.debug("This is a DEBUG log message", key_1="value_1", key_2="value_2", key_n="value_n")
    logger.info("This is an INFO log message", key_1="value_1", key_2="value_2", key_n="value_n")
    logger.warning("This is a WARNING log message", key_1="value_1", key_2="value_2", key_n="value_n")
    logger.error("This is an ERROR log message", key_1="value_1", key_2="value_2", key_n="value_n")
    logger.critical("This is a CRITICAL log message", key_1="value_1", key_2="value_2", key_n="value_n")

    try:
        1 / 0
    except ZeroDivisionError:
        logger.exception("An EXCEPTION log with stack trace occurred", key_1="value_1", key_2="value_2")


```
![basic example](images/plain_console_logger.png)


In production, you should aim for structured, machine-readable logs that can be easily ingested by log aggregation and monitoring tools like ELK (Elasticsearch, Logstash, Kibana), Datadog, or Prometheus:

```python
    from seismos_package.logging import LoggingConfigurator
    from seismos_package.config import SeismosConfig
    import structlog

    config = SeismosConfig()

    LoggingConfigurator(
        service_name=config.APP_NAME,
        log_level='INFO',
        setup_logging_dict=True
    ).configure_structlog(
        formatter='json_formatter',
        formatter_std_lib='json_formatter'
    )

    logger = structlog.get_logger(__name__)
    logger.debug("This is a DEBUG log message", key_1="value_1", key_2="value_2", key_n="value_n")
    logger.info("This is an INFO log message", key_1="value_1", key_2="value_2", key_n="value_n")
    logger.warning("This is a WARNING log message", key_1="value_1", key_2="value_2", key_n="value_n")
    logger.error("This is an ERROR log message", key_1="value_1", key_2="value_2", key_n="value_n")
    logger.critical("This is a CRITICAL log message", key_1="value_1", key_2="value_2", key_n="value_n")

    try:
        1 / 0
    except ZeroDivisionError:
        logger.exception("An EXCEPTION log with stack trace occurred", key_1="value_1", key_2="value_2")
```

![logger with different keys](images/json_logger.png)


## Middleware for automatic logging context

The middleware adds request_id, IP, and user_id to every log during a request/response cycle.
This middleware module provides logging context management for both Flask and FastAPI applications using structlog.

Flask Middleware (add_request_context_flask): Captures essential request data such as the request ID, method, and path, binding them to the structlog context for better traceability during the request lifecycle.

FastAPI: Use the ASGI RequestLoggingMiddleware for structured request/response logging and request context propagation. Legacy helpers (add_request_context_fastapi and FastAPIRequestContextMiddleware) were removed; migrate to RequestLoggingMiddleware.

This setup ensures structured, consistent logging across both frameworks, improving traceability and debugging in distributed systems.


This guide explains how to set up and use structlog for structured logging in a Flask application. The goal is to have a consistent and centralized logging setup that can be reused across the application.
The logger is initialized once in the main application file (e.g., app.py).

```python
from flask import Flask
from seismos_package.logging.middlewares import FlaskRequestContextMiddleware
from seismos_package.logging import LoggingConfigurator
from seismos_package.config import SeismosConfig
import structlog

config = SeismosConfig()
LoggingConfigurator(
    service_name=config.APP_NAME,
    log_level="INFO",
    setup_logging_dict=True,
).configure_structlog(formatter='json_formatter', formatter_std_lib='json_formatter')

app = Flask(__name__)
# Wrap the WSGI app to bind request context (id, method, path) into structlog
app.wsgi_app = FlaskRequestContextMiddleware(app.wsgi_app)

logger = structlog.get_logger(__name__)

@app.route("/")
def home():
    logger.info("Context set for request")
    return "Hello, World!"
```

![logger with context flask](images/flask_logger_with_context.png)

You can use the same logger instance across different modules by importing structlog directly.
Example (services.py):


```python
    import structlog

    logger = structlog.get_logger(__name__)
    logger.info("Processing data started", data_size=100)
```
Key Points:

- Centralized Configuration: The logger is initialized once in app.py.
- Consistent Usage: structlog.get_logger(__name__) is imported and used across all files.
- Context Management: Context is managed using structlog.contextvars.bind_contextvars().
- Structured Logging: The JSON formatter ensures logs are machine-readable.

### FastAPI quick start

```python
from fastapi import FastAPI
from seismos_package.logging.middlewares import RequestLoggingMiddleware

app = FastAPI()
app.add_middleware(
    RequestLoggingMiddleware,
    slow_request_threshold_ms=2000,
    propagate_request_id=True,
    # optional tuning
    skip_paths={"/healthz", "/metrics"},
    skip_prefixes=("/metrics",),
    sample_2xx_rate=0.5,  # sample successful 2xx/3xx
)
```

#### Key behaviors
- Request ID priority: `traceparent` → `X-Request-Id` → `X-Amzn-Trace-Id` → UUID
- Response headers propagation (when propagate_request_id=True):
  - Always sets `X-Request-Id` (if not present)
  - If `traceparent` was present in the request, it is also added back to the response
- Skipping noisy traffic: OPTIONS, exact `skip_paths`, and any path starting with items in `skip_prefixes`
- Slow requests: end log includes `slow_request` (bool) and `slow_threshold_ms` (int|None)
- Sensitive header sanitization:
  - `Authorization` preserves scheme only (e.g., `Bearer ***`)
  - `Cookie`, `Set-Cookie`, `X-API-Key` are redacted as `<redacted>`
- Streaming-safe: response_size accumulates across multiple `http.response.body` messages; request/response bodies are never read/buffered by the middleware

#### Suggested production defaults
- `slow_request_threshold_ms=2000`
- `skip_paths={"/healthz", "/metrics"}` and `skip_prefixes=("/metrics",)`
- `sample_2xx_rate` between 0.1 and 0.5 depending on expected traffic

#### Testing status
100% coverage across seismos_package; unit tests cover sampling (on/off), 2xx/3xx/4xx/5xx, exceptions before response start, large uploads (middleware doesn’t read body), streaming responses, skip rules, and header propagation.



Automatic injection of:
-   user_id
-   IP
-   request_id
-  request_method


This a console view, in prod it will be json (using python json logging to have standard logging and structlog logging as close as possible)


## Why use structured logging
-   Standard logging often outputs plain text logs, which can be challenging for log aggregation tools like EFK Stack or Grafana Loki to process effectively.
-   Structured logging outputs data in a machine-readable format (e.g., JSON), making it easier for log analysis tools to filter and process logs efficiently.
-   With structured logging, developers can filter logs by fields such as request_id, user_id, and transaction_id for better traceability across distributed systems.
-   The primary goal is to simplify debugging, enable better error tracking, and improve observability with enhanced log analysis capabilities.
-   Structured logs are designed to be consumed primarily by machines for monitoring and analytics, while still being readable for developers when needed.
-   This package leverages structlog, a library that enhances Python's standard logging by providing better context management and a flexible structure for log messages.


# Development of this project

Please install [poetry](https://python-poetry.org/docs/#installation) as this is the tool we use for releasing and development.

    poetry install && poetry run pytest -rs --cov=seismos_package -s

To run tests inside docker:

    poetry install --with dev && poetry run pytest -rs --cov=seismos_package

To run pre-commit:
    poetry run pre-commit run --all-files


## ASGI Request Logging Middleware (FastAPI/Starlette)

The package ships a production-friendly ASGI middleware that logs each HTTP request/response using structlog.
It is safe for large payloads and streaming responses (e.g., CSV), because it does not buffer bodies.

Key features:
- Severity by status: 2xx/3xx (info), 4xx (warning), 5xx (error)
- Response size counting without reading the body
- Sanitized headers (Authorization shows scheme only: e.g., "Bearer ***")
- Request correlation with X-Request-Id propagation
- Skip noisy paths and sample successful requests

Minimal setup in FastAPI:

```python
from fastapi import FastAPI
from seismos_package.logging import LoggingConfigurator
from seismos_package.config import SeismosConfig
from seismos_package.logging.middlewares import RequestLoggingMiddleware

config = SeismosConfig()
LoggingConfigurator(
    service_name=config.APP_NAME,
    log_level="INFO",
    setup_logging_dict=True,
).configure_structlog(formatter="json_formatter", formatter_std_lib="json_formatter")

app = FastAPI()

# Add logging middleware last (outermost) so it wraps all handlers and middlewares
app.add_middleware(
    RequestLoggingMiddleware,
    slow_request_threshold_ms=2000,      # optional: warn on slow requests
    propagate_request_id=True,           # adds X-Request-Id if missing
    skip_paths={"/healthz", "/metrics"}, # skip noisy endpoints entirely
    sample_2xx_rate=0.2,                 # log roughly 20% of successful requests
)
```

### What gets logged
- Start: method, path, query, client_ip, user_agent, request sizes/types (no body)
- End: status_code, duration_ms, response_size, response content type, and request metadata
- Errors (4xx/5xx): includes sanitized request/response headers; unexpected exceptions include traceback

### Notes
- "Skip paths" disables both start/end logs for the given paths
- Sampling applies only to 2xx/3xx end logs; errors are always logged
- Request ID priority: X-Request-Id, then traceparent, then X-Amzn-Trace-Id; the chosen ID is propagated back as X-Request-Id

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "seismos-package",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<3.13,>=3.8",
    "maintainer_email": null,
    "keywords": "logging, utilities, internal-package",
    "author": "Seismos",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/ce/91/f171c52aa6a93ca1b1a5e0a6cd457da8cb1dda4725ef8a167e6075b2b9e8/seismos_package-0.1.9.tar.gz",
    "platform": null,
    "description": "# Seismos Python Package\n\n## Table of contents\n- [Structured logging with structlog](#structured-logging-with-structlog)\n  - [Basic configuration](#basic-configuration)\n- [Middleware for automatic logging context](#middleware-for-automatic-logging-context)\n  - [FastAPI quick start](#fastapi-quick-start)\n- [ASGI Request Logging Middleware (FastAPI/Starlette)](#asgi-request-logging-middleware-fastapistarlette)\n  - [What gets logged](#what-gets-logged)\n  - [Notes](#notes)\n- [Why use structured logging](#why-use-structured-logging)\n- [Development of this project](#development-of-this-project)\n\n\n## Structured logging with structlog\nStructlog is a powerful logging library for structured, context-aware logging.\nMore details can be found in the [structlog](https://www.structlog.org/en/stable/).\n\n### Basic configuration\n\ninstead of `logger = logging.getLogger(__name__)` it is `logger = structlog.get_logger(__name__)`\n\n```python\n    from seismos_package.logging import LoggingConfigurator\n    from seismos_package.config import SeismosConfig\n    import structlog\n\n    config = SeismosConfig()\n\n    LoggingConfigurator(\n        service_name=config.APP_NAME,\n        log_level='INFO',\n        setup_logging_dict=True\n    ).configure_structlog(\n        formatter='plain_console',\n        formatter_std_lib='plain_console'\n    )\n\n    logger = structlog.get_logger(__name__)\n    logger.debug(\"This is a DEBUG log message\", key_1=\"value_1\", key_2=\"value_2\", key_n=\"value_n\")\n    logger.info(\"This is an INFO log message\", key_1=\"value_1\", key_2=\"value_2\", key_n=\"value_n\")\n    logger.warning(\"This is a WARNING log message\", key_1=\"value_1\", key_2=\"value_2\", key_n=\"value_n\")\n    logger.error(\"This is an ERROR log message\", key_1=\"value_1\", key_2=\"value_2\", key_n=\"value_n\")\n    logger.critical(\"This is a CRITICAL log message\", key_1=\"value_1\", key_2=\"value_2\", key_n=\"value_n\")\n\n    try:\n        1 / 0\n    except ZeroDivisionError:\n        logger.exception(\"An EXCEPTION log with stack trace occurred\", key_1=\"value_1\", key_2=\"value_2\")\n\n\n```\n![basic example](images/plain_console_logger.png)\n\n\nIn production, you should aim for structured, machine-readable logs that can be easily ingested by log aggregation and monitoring tools like ELK (Elasticsearch, Logstash, Kibana), Datadog, or Prometheus:\n\n```python\n    from seismos_package.logging import LoggingConfigurator\n    from seismos_package.config import SeismosConfig\n    import structlog\n\n    config = SeismosConfig()\n\n    LoggingConfigurator(\n        service_name=config.APP_NAME,\n        log_level='INFO',\n        setup_logging_dict=True\n    ).configure_structlog(\n        formatter='json_formatter',\n        formatter_std_lib='json_formatter'\n    )\n\n    logger = structlog.get_logger(__name__)\n    logger.debug(\"This is a DEBUG log message\", key_1=\"value_1\", key_2=\"value_2\", key_n=\"value_n\")\n    logger.info(\"This is an INFO log message\", key_1=\"value_1\", key_2=\"value_2\", key_n=\"value_n\")\n    logger.warning(\"This is a WARNING log message\", key_1=\"value_1\", key_2=\"value_2\", key_n=\"value_n\")\n    logger.error(\"This is an ERROR log message\", key_1=\"value_1\", key_2=\"value_2\", key_n=\"value_n\")\n    logger.critical(\"This is a CRITICAL log message\", key_1=\"value_1\", key_2=\"value_2\", key_n=\"value_n\")\n\n    try:\n        1 / 0\n    except ZeroDivisionError:\n        logger.exception(\"An EXCEPTION log with stack trace occurred\", key_1=\"value_1\", key_2=\"value_2\")\n```\n\n![logger with different keys](images/json_logger.png)\n\n\n## Middleware for automatic logging context\n\nThe middleware adds request_id, IP, and user_id to every log during a request/response cycle.\nThis middleware module provides logging context management for both Flask and FastAPI applications using structlog.\n\nFlask Middleware (add_request_context_flask): Captures essential request data such as the request ID, method, and path, binding them to the structlog context for better traceability during the request lifecycle.\n\nFastAPI: Use the ASGI RequestLoggingMiddleware for structured request/response logging and request context propagation. Legacy helpers (add_request_context_fastapi and FastAPIRequestContextMiddleware) were removed; migrate to RequestLoggingMiddleware.\n\nThis setup ensures structured, consistent logging across both frameworks, improving traceability and debugging in distributed systems.\n\n\nThis guide explains how to set up and use structlog for structured logging in a Flask application. The goal is to have a consistent and centralized logging setup that can be reused across the application.\nThe logger is initialized once in the main application file (e.g., app.py).\n\n```python\nfrom flask import Flask\nfrom seismos_package.logging.middlewares import FlaskRequestContextMiddleware\nfrom seismos_package.logging import LoggingConfigurator\nfrom seismos_package.config import SeismosConfig\nimport structlog\n\nconfig = SeismosConfig()\nLoggingConfigurator(\n    service_name=config.APP_NAME,\n    log_level=\"INFO\",\n    setup_logging_dict=True,\n).configure_structlog(formatter='json_formatter', formatter_std_lib='json_formatter')\n\napp = Flask(__name__)\n# Wrap the WSGI app to bind request context (id, method, path) into structlog\napp.wsgi_app = FlaskRequestContextMiddleware(app.wsgi_app)\n\nlogger = structlog.get_logger(__name__)\n\n@app.route(\"/\")\ndef home():\n    logger.info(\"Context set for request\")\n    return \"Hello, World!\"\n```\n\n![logger with context flask](images/flask_logger_with_context.png)\n\nYou can use the same logger instance across different modules by importing structlog directly.\nExample (services.py):\n\n\n```python\n    import structlog\n\n    logger = structlog.get_logger(__name__)\n    logger.info(\"Processing data started\", data_size=100)\n```\nKey Points:\n\n- Centralized Configuration: The logger is initialized once in app.py.\n- Consistent Usage: structlog.get_logger(__name__) is imported and used across all files.\n- Context Management: Context is managed using structlog.contextvars.bind_contextvars().\n- Structured Logging: The JSON formatter ensures logs are machine-readable.\n\n### FastAPI quick start\n\n```python\nfrom fastapi import FastAPI\nfrom seismos_package.logging.middlewares import RequestLoggingMiddleware\n\napp = FastAPI()\napp.add_middleware(\n    RequestLoggingMiddleware,\n    slow_request_threshold_ms=2000,\n    propagate_request_id=True,\n    # optional tuning\n    skip_paths={\"/healthz\", \"/metrics\"},\n    skip_prefixes=(\"/metrics\",),\n    sample_2xx_rate=0.5,  # sample successful 2xx/3xx\n)\n```\n\n#### Key behaviors\n- Request ID priority: `traceparent` \u2192 `X-Request-Id` \u2192 `X-Amzn-Trace-Id` \u2192 UUID\n- Response headers propagation (when propagate_request_id=True):\n  - Always sets `X-Request-Id` (if not present)\n  - If `traceparent` was present in the request, it is also added back to the response\n- Skipping noisy traffic: OPTIONS, exact `skip_paths`, and any path starting with items in `skip_prefixes`\n- Slow requests: end log includes `slow_request` (bool) and `slow_threshold_ms` (int|None)\n- Sensitive header sanitization:\n  - `Authorization` preserves scheme only (e.g., `Bearer ***`)\n  - `Cookie`, `Set-Cookie`, `X-API-Key` are redacted as `<redacted>`\n- Streaming-safe: response_size accumulates across multiple `http.response.body` messages; request/response bodies are never read/buffered by the middleware\n\n#### Suggested production defaults\n- `slow_request_threshold_ms=2000`\n- `skip_paths={\"/healthz\", \"/metrics\"}` and `skip_prefixes=(\"/metrics\",)`\n- `sample_2xx_rate` between 0.1 and 0.5 depending on expected traffic\n\n#### Testing status\n100% coverage across seismos_package; unit tests cover sampling (on/off), 2xx/3xx/4xx/5xx, exceptions before response start, large uploads (middleware doesn\u2019t read body), streaming responses, skip rules, and header propagation.\n\n\n\nAutomatic injection of:\n-   user_id\n-   IP\n-   request_id\n-  request_method\n\n\nThis a console view, in prod it will be json (using python json logging to have standard logging and structlog logging as close as possible)\n\n\n## Why use structured logging\n-   Standard logging often outputs plain text logs, which can be challenging for log aggregation tools like EFK Stack or Grafana Loki to process effectively.\n-   Structured logging outputs data in a machine-readable format (e.g., JSON), making it easier for log analysis tools to filter and process logs efficiently.\n-   With structured logging, developers can filter logs by fields such as request_id, user_id, and transaction_id for better traceability across distributed systems.\n-   The primary goal is to simplify debugging, enable better error tracking, and improve observability with enhanced log analysis capabilities.\n-   Structured logs are designed to be consumed primarily by machines for monitoring and analytics, while still being readable for developers when needed.\n-   This package leverages structlog, a library that enhances Python's standard logging by providing better context management and a flexible structure for log messages.\n\n\n# Development of this project\n\nPlease install [poetry](https://python-poetry.org/docs/#installation) as this is the tool we use for releasing and development.\n\n    poetry install && poetry run pytest -rs --cov=seismos_package -s\n\nTo run tests inside docker:\n\n    poetry install --with dev && poetry run pytest -rs --cov=seismos_package\n\nTo run pre-commit:\n    poetry run pre-commit run --all-files\n\n\n## ASGI Request Logging Middleware (FastAPI/Starlette)\n\nThe package ships a production-friendly ASGI middleware that logs each HTTP request/response using structlog.\nIt is safe for large payloads and streaming responses (e.g., CSV), because it does not buffer bodies.\n\nKey features:\n- Severity by status: 2xx/3xx (info), 4xx (warning), 5xx (error)\n- Response size counting without reading the body\n- Sanitized headers (Authorization shows scheme only: e.g., \"Bearer ***\")\n- Request correlation with X-Request-Id propagation\n- Skip noisy paths and sample successful requests\n\nMinimal setup in FastAPI:\n\n```python\nfrom fastapi import FastAPI\nfrom seismos_package.logging import LoggingConfigurator\nfrom seismos_package.config import SeismosConfig\nfrom seismos_package.logging.middlewares import RequestLoggingMiddleware\n\nconfig = SeismosConfig()\nLoggingConfigurator(\n    service_name=config.APP_NAME,\n    log_level=\"INFO\",\n    setup_logging_dict=True,\n).configure_structlog(formatter=\"json_formatter\", formatter_std_lib=\"json_formatter\")\n\napp = FastAPI()\n\n# Add logging middleware last (outermost) so it wraps all handlers and middlewares\napp.add_middleware(\n    RequestLoggingMiddleware,\n    slow_request_threshold_ms=2000,      # optional: warn on slow requests\n    propagate_request_id=True,           # adds X-Request-Id if missing\n    skip_paths={\"/healthz\", \"/metrics\"}, # skip noisy endpoints entirely\n    sample_2xx_rate=0.2,                 # log roughly 20% of successful requests\n)\n```\n\n### What gets logged\n- Start: method, path, query, client_ip, user_agent, request sizes/types (no body)\n- End: status_code, duration_ms, response_size, response content type, and request metadata\n- Errors (4xx/5xx): includes sanitized request/response headers; unexpected exceptions include traceback\n\n### Notes\n- \"Skip paths\" disables both start/end logs for the given paths\n- Sampling applies only to 2xx/3xx end logs; errors are always logged\n- Request ID priority: X-Request-Id, then traceparent, then X-Amzn-Trace-Id; the chosen ID is propagated back as X-Request-Id\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Seismos internal Python package for structured logging and utilities.",
    "version": "0.1.9",
    "project_urls": null,
    "split_keywords": [
        "logging",
        " utilities",
        " internal-package"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "51af49ce8b14e8a3b3e63b714bf0171025b2bd1426c8fa6b321f6378d7d6dbb9",
                "md5": "eba4d119912a99cc71b13bb76c6c4749",
                "sha256": "f77ef1f3dde18527846ad81d3008983827fd98503ca03f244aaf4ccdaf3078f0"
            },
            "downloads": -1,
            "filename": "seismos_package-0.1.9-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "eba4d119912a99cc71b13bb76c6c4749",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<3.13,>=3.8",
            "size": 11121,
            "upload_time": "2025-10-10T12:43:41",
            "upload_time_iso_8601": "2025-10-10T12:43:41.997050Z",
            "url": "https://files.pythonhosted.org/packages/51/af/49ce8b14e8a3b3e63b714bf0171025b2bd1426c8fa6b321f6378d7d6dbb9/seismos_package-0.1.9-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "ce91f171c52aa6a93ca1b1a5e0a6cd457da8cb1dda4725ef8a167e6075b2b9e8",
                "md5": "6560f7ea267245aef41337142a0ea317",
                "sha256": "db03d0392707ab2593e0802b3bf26b1992802f85738e3cfff9f85d0a47ffa5a9"
            },
            "downloads": -1,
            "filename": "seismos_package-0.1.9.tar.gz",
            "has_sig": false,
            "md5_digest": "6560f7ea267245aef41337142a0ea317",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<3.13,>=3.8",
            "size": 13165,
            "upload_time": "2025-10-10T12:43:43",
            "upload_time_iso_8601": "2025-10-10T12:43:43.095182Z",
            "url": "https://files.pythonhosted.org/packages/ce/91/f171c52aa6a93ca1b1a5e0a6cd457da8cb1dda4725ef8a167e6075b2b9e8/seismos_package-0.1.9.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-10 12:43:43",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "seismos-package"
}
        
Elapsed time: 2.25107s