libdev
Advanced tools
| Metadata-Version: 2.1 | ||
| Name: libdev | ||
| Version: 0.95 | ||
| Version: 0.96 | ||
| Summary: Set of standard functions for development | ||
@@ -5,0 +5,0 @@ Home-page: https://github.com/chilleco/lib |
@@ -5,4 +5,4 @@ """ | ||
| __version__ = "0.95" | ||
| __version__ = "0.96" | ||
| __all__ = ("__version__",) |
+23
-4
@@ -0,4 +1,9 @@ | ||
| """Centralized configuration loader for LibDev consumers. | ||
| This module mirrors the behavior documented in `LIBDEV_DOCUMENTATION.md`: | ||
| it first ingests a project level ``sets.json`` file, then overlays values | ||
| from ``.env`` (via ``python-dotenv``) by translating dotted keys to | ||
| ``UPPER_SNAKE_CASE`` environment variables. Use the helpers below instead of | ||
| calling ``os.getenv`` throughout the codebase so the hierarchy stays uniform. | ||
| """ | ||
| Functionality of getting configuration | ||
| """ | ||
@@ -22,4 +27,12 @@ import os | ||
| def cfg(name, default=None): | ||
| """Get config value by key""" | ||
| """Return a config value stored in ``sets.json``/``.env``. | ||
| The lookup walks dotted paths inside the parsed JSON structure and, when a | ||
| key is missing, falls back to an environment variable where dots are | ||
| replaced with underscores and the string is upper-cased (``api.base`` → | ||
| ``API_BASE``). Environment values are JSON-decoded automatically so booleans | ||
| and numeric strings turn into native Python types. ``default`` is returned | ||
| when a key is absent in both sources. | ||
| """ | ||
| keys = name.split(".") | ||
@@ -48,4 +61,10 @@ data = sets | ||
| def set_cfg(name, value): | ||
| """Set config value""" | ||
| """Mutate the in-memory ``sets`` dictionary for tests or overrides. | ||
| Writes scoped dotted keys back into the ``sets`` mapping without touching | ||
| disk. This mirrors the behavior in consumer repos that temporarily adjust | ||
| configuration for integration tests or AI agents. Changes live only for the | ||
| current process and should be reset between tests. | ||
| """ | ||
| array_name = name.split(".") | ||
@@ -52,0 +71,0 @@ dictionary = {} |
+1
-0
@@ -242,2 +242,3 @@ """ | ||
| def clear_text(data, extra=".,"): | ||
| """Strip all characters except alphanumerics, space, and ``extra`` chars.""" | ||
| return re.sub(rf"[^\w {extra}]", "", data).strip() |
+11
-3
@@ -0,4 +1,7 @@ | ||
| """Development-environment helpers tied to LibDev guidelines. | ||
| Currently exposes public IP validation logic that mirrors the rules documented | ||
| in ``LIBDEV_DOCUMENTATION.md`` for analytics and logging pipelines. Extend this | ||
| module instead of sprinkling regex checks throughout consumer projects. | ||
| """ | ||
| Development tools | ||
| """ | ||
@@ -9,4 +12,9 @@ import re | ||
| def check_public_ip(ip): | ||
| """Check if the IP address is public""" | ||
| """Return ``ip`` if it is routable on the public internet. | ||
| Private (RFC1918), loopback, and carrier-grade NAT subnets are filtered | ||
| out, ensuring only analyzable public addresses pass downstream. ``None`` is | ||
| returned for empty inputs or addresses that match reserved ranges. | ||
| """ | ||
| if not ip: | ||
@@ -13,0 +21,0 @@ return None |
+16
-4
@@ -0,4 +1,7 @@ | ||
| """Utility helpers for dealing with document/JSON serialization. | ||
| These functions are referenced by the integration guide to make sure assets | ||
| and structured logs look the same across repositories (e.g., ``log.json`` uses | ||
| ``to_json`` under the hood). | ||
| """ | ||
| Documents processing functionality | ||
| """ | ||
@@ -10,3 +13,8 @@ import base64 | ||
| def to_base64(image, mime="image/jpg"): | ||
| """Convert image to base64""" | ||
| """Return a ``data:`` URL for the given file-like ``image``. | ||
| Reads the stream, base64-encodes the bytes, and prefixes it with the MIME | ||
| Type so downstream clients (frontends, bots) can embed the payload directly | ||
| without touching disk. | ||
| """ | ||
| data = base64.b64encode(image.read()) | ||
@@ -17,3 +25,7 @@ return f"data:{mime};base64,{data.decode('utf-8')}" | ||
| def to_json(data): | ||
| """Convert object to json""" | ||
| """Serialize ``data`` to a UTF-8 friendly JSON string. | ||
| Uses tab indentation and ``ensure_ascii=False`` to preserve Cyrillic or | ||
| emoji content mentioned in ``LIBDEV_DOCUMENTATION.md``. | ||
| """ | ||
| return json.dumps(data, indent="\t", ensure_ascii=False) |
+31
-10
@@ -0,4 +1,7 @@ | ||
| """Numeric normalization and presentation helpers used across LibDev. | ||
| Implements the opinionated formatting rules discussed in the integration | ||
| guide: deterministic rounding, removal of floating-point artifacts, thousands | ||
| separators, and zero-compression for compact analytical displays. | ||
| """ | ||
| Numbers functionality | ||
| """ | ||
@@ -25,3 +28,3 @@ import re | ||
| def to_num(value) -> bool: | ||
| """Convert value to int or float""" | ||
| """Convert an incoming scalar to ``int``/``float`` while preserving intent.""" | ||
@@ -84,4 +87,8 @@ if value is None: | ||
| def simplify_value(value, decimals=4): | ||
| """Get the significant part of a number""" | ||
| """Return the significant digits of ``value`` capped by ``decimals``. | ||
| Used by analytics pipelines to produce short strings that still encode the | ||
| important portion of very large or tiny numbers. | ||
| """ | ||
| if value is None: | ||
@@ -131,4 +138,10 @@ return None | ||
| ): | ||
| """Decorate the number beautifully""" | ||
| """Format ``value`` according to LibDev UI/metrics rules. | ||
| Supports optional rounding to a target precision, manual sign prefixing, | ||
| swapping the thousands separator symbol, and compressing leading/trailing | ||
| zeros (see ``compress_zeros``). This helper is the canonical way to build | ||
| user-facing number strings. | ||
| """ | ||
| if value is None: | ||
@@ -288,2 +301,4 @@ return None | ||
| def to_plain(value) -> str: | ||
| """Convert ``value`` to a normalized decimal string without notation.""" | ||
| if value is None: | ||
@@ -318,8 +333,14 @@ return None | ||
| def compress_zeros(x, zeros=2, round=None) -> str: | ||
| """Compress zero runs using the subscript notation referenced in the docs. | ||
| Examples:: | ||
| 0.000012 -> "0.0₄12" | ||
| 1.000045 -> "1.0₄45" | ||
| ``round`` controls how many digits remain after the compressed block, while | ||
| ``zeros`` sets the minimum run length required before a compression occurs. | ||
| Returns a string that can be passed to ``pretty`` or directly displayed in | ||
| dashboards. | ||
| """ | ||
| 0.000012 -> '0.0₄12' | ||
| 1.000045 -> '1.0₄45' | ||
| round: number of digits after the zero block (rounds). | ||
| zeros: minimum count of consecutive zeros to compress (default: 2). | ||
| """ | ||
@@ -326,0 +347,0 @@ if x is None: |
+14
-19
@@ -0,4 +1,8 @@ | ||
| """Async HTTP gateway used across LibDev powered projects. | ||
| Centralizes ``aiohttp`` usage so upstream services benefit from the same request | ||
| construction (JSON vs form payloads, multipart file uploads) and response | ||
| parsing rules described in ``LIBDEV_DOCUMENTATION.md``. Always ``await`` the | ||
| helpers in this module to stay inside the async boundary. | ||
| """ | ||
| Provides an asynchronous function to fetch data from a URL using aiohttp | ||
| """ | ||
@@ -17,20 +21,11 @@ import aiohttp | ||
| ): | ||
| """ | ||
| Fetch data from a URL using aiohttp. | ||
| """Perform an HTTP request and normalize the response payload. | ||
| Args: | ||
| url (str): The URL to fetch data from. | ||
| payload (dict, optional): The payload to send with the request. | ||
| Defaults to None. | ||
| type_req (str, optional): The type of request (e.g., 'post', 'put', | ||
| 'delete', etc.). Defaults to 'post'. | ||
| type_data (str, optional): The type of data (e.g., 'json', 'data'). | ||
| Defaults to 'json'. | ||
| headers (dict, optional): The headers to include with the request. | ||
| Defaults to None. | ||
| timeout (float, optional): The timeout for the request in seconds. | ||
| Defaults to None. | ||
| Returns: | ||
| tuple: A tuple containing the status code and the response data. | ||
| Parameters mirror the rules from the integration guide: ``files`` may be a | ||
| mapping of field name to bytes/file-like objects (forcing multipart form | ||
| uploads), ``type_data`` controls whether the ``payload`` is supplied via the | ||
| ``json`` or ``data`` keyword, and ``type_req`` is the lowercase HTTP verb. A | ||
| status code integer and the decoded response body (JSON dict, plain text, or | ||
| raw bytes) are returned. Callers are expected to provide their own retries | ||
| or circuit breaking logic. | ||
| """ | ||
@@ -37,0 +32,0 @@ if payload is None: |
+51
-12
@@ -0,4 +1,7 @@ | ||
| """Datetime parsing/formatting helpers aligned with LibDev localization rules. | ||
| The functions below implement the Russian month parsing, timezone handling, and | ||
| delta formatting conventions cited in ``LIBDEV_DOCUMENTATION.md`` so every | ||
| project surfaces dates the same way. | ||
| """ | ||
| Time functionality | ||
| """ | ||
@@ -54,3 +57,3 @@ # TODO: Учитывать летнее / зимнее время в прошлых датах, которого теперь нет | ||
| def get_time(data=None, template="%d.%m.%Y %H:%M:%S", tz=0): | ||
| def get_time(data=None, template="%d.%m.%Y %H:%M:%S", tz=None): | ||
| """Get time from timestamp""" | ||
@@ -62,2 +65,4 @@ | ||
| return data | ||
| if tz is None: | ||
| tz = 0 | ||
@@ -72,3 +77,3 @@ # TODO: smart TZ | ||
| def get_date(data=None, template="%d.%m.%Y", tz=0): | ||
| def get_date(data=None, template="%d.%m.%Y", tz=None): | ||
| """Get date from timestamp""" | ||
@@ -78,3 +83,3 @@ return get_time(data, template, tz) | ||
| def decode_time(data=None, template="%d.%m.%Y %H:%M:%S", tz=0): | ||
| def decode_time(data=None, template="%d.%m.%Y %H:%M:%S", tz=None): | ||
| """Get timestamp from time""" | ||
@@ -86,2 +91,4 @@ | ||
| return data | ||
| if tz is None: | ||
| tz = 0 | ||
@@ -98,3 +105,3 @@ try: | ||
| def decode_date(data=None, template="%d.%m.%Y", tz=0): | ||
| def decode_date(data=None, template="%d.%m.%Y", tz=None): | ||
| """Get timestamp from date""" | ||
@@ -105,3 +112,3 @@ return decode_time(data, template, tz) | ||
| # pylint: disable=too-many-branches,too-many-statements | ||
| def parse_time(data: str, tz=0): | ||
| def parse_time(data: str, tz=None): | ||
| """Parse time""" | ||
@@ -111,2 +118,5 @@ | ||
| if tz is None: | ||
| tz = 0 | ||
| data = data.lower() | ||
@@ -275,3 +285,3 @@ | ||
| def get_midnight(timestamp=None, tz=0): | ||
| def get_midnight(timestamp=None, tz=None): | ||
| """ | ||
@@ -287,10 +297,15 @@ Get the start of the day (midnight) for a given timestamp in a specified timezone. | ||
| """ | ||
| if timestamp is None: | ||
| timestamp = time.time() | ||
| if tz is None: | ||
| tz = 0 | ||
| dt_local = datetime.datetime.fromtimestamp(timestamp, tz=to_tz(tz)) | ||
| start_day = dt_local.replace(hour=0, minute=0, second=0, microsecond=0) | ||
| return int(start_day.timestamp()) | ||
| def get_month_start(timestamp=None, tz=0): | ||
| def get_month_start(timestamp=None, tz=None): | ||
| """ | ||
@@ -306,10 +321,21 @@ Get the start of the month (midnight on the first day of the month) for a given timestamp in a specified timezone. | ||
| """ | ||
| if timestamp is None: | ||
| timestamp = time.time() | ||
| if tz is None: | ||
| tz = 0 | ||
| dt_local = datetime.datetime.fromtimestamp(timestamp, tz=to_tz(tz)) | ||
| start_month = dt_local.replace(day=1, hour=0, minute=0, second=0, microsecond=0) | ||
| return int(start_month.timestamp()) | ||
| def get_week_start(timestamp=None, tz=0): | ||
| def get_previous_month(timestamp=None, tz=None): | ||
| current_period = get_month_start(timestamp, tz) | ||
| one_month_ago = get_month_start(current_period - 1, tz) | ||
| return one_month_ago | ||
| def get_week_start(timestamp=None, tz=None): | ||
| """ | ||
@@ -325,13 +351,19 @@ Get the start of the week (midnight on Monday) for a given timestamp in a specified timezone. | ||
| """ | ||
| if timestamp is None: | ||
| timestamp = time.time() | ||
| if tz is None: | ||
| tz = 0 | ||
| dt_local = datetime.datetime.fromtimestamp(timestamp, tz=to_tz(tz)) | ||
| # Calculate days to subtract to get to Monday (weekday() returns 0 for Monday, 6 for Sunday) | ||
| days_since_monday = dt_local.weekday() | ||
| start_week = dt_local - datetime.timedelta(days=days_since_monday) | ||
| start_week = start_week.replace(hour=0, minute=0, second=0, microsecond=0) | ||
| return int(start_week.timestamp()) | ||
| def get_next_day(timestamp=None, tz=0): | ||
| def get_next_day(timestamp=None, tz=None): | ||
| """ | ||
@@ -347,4 +379,8 @@ Get the start of the next day (midnight) for a given timestamp in a specified timezone. | ||
| """ | ||
| if timestamp is None: | ||
| timestamp = time.time() | ||
| if tz is None: | ||
| tz = 0 | ||
| dt_local = datetime.datetime.fromtimestamp(timestamp, tz=to_tz(tz)) | ||
@@ -354,2 +390,3 @@ next_day = (dt_local + datetime.timedelta(days=1)).replace( | ||
| ) | ||
| return int(next_day.timestamp()) | ||
@@ -359,3 +396,3 @@ | ||
| # TODO: get previous month (params=-1 +1) | ||
| def get_next_month(timestamp=None, tz=0): | ||
| def get_next_month(timestamp=None, tz=None): | ||
| """ | ||
@@ -374,2 +411,4 @@ Get the start of the next month (midnight on the first day of the next month) for a given timestamp in a specified timezone. | ||
| timestamp = time.time() | ||
| if tz is None: | ||
| tz = 0 | ||
@@ -376,0 +415,0 @@ dt_local = datetime.datetime.fromtimestamp(timestamp, tz=to_tz(tz)) |
+1
-1
| Metadata-Version: 2.1 | ||
| Name: libdev | ||
| Version: 0.95 | ||
| Version: 0.96 | ||
| Summary: Set of standard functions for development | ||
@@ -5,0 +5,0 @@ Home-page: https://github.com/chilleco/lib |
@@ -160,2 +160,3 @@ from libdev.num import ( | ||
| assert pretty(12345.6, 3, True) == "+12’346" | ||
| assert pretty(1046012.4859999998, 0) == "1’046’012" | ||
| assert pretty(-0.000000235235, zeros=None, compress=None) == "-0.000000235235" | ||
@@ -162,0 +163,0 @@ assert pretty(-0.000000235235, zeros=4, compress=2) == "-0.0₆24" |
@@ -16,2 +16,3 @@ """ | ||
| get_month_start, | ||
| get_previous_month, | ||
| get_next_day, | ||
@@ -125,2 +126,8 @@ get_next_month, | ||
| def test_get_previous_month(): | ||
| assert get_previous_month() == 1759276800 | ||
| assert get_previous_month(tz=3) == 1759266000 | ||
| assert get_previous_month(1760648400, 1) == 1756681200 | ||
| def test_get_next_day(): | ||
@@ -127,0 +134,0 @@ assert get_next_day(1704060061) == 1704067200 |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
111204
4.33%2966
2.38%