From 9045da27ffeb0a8628d99dd9a5045b67d9eca46e Mon Sep 17 00:00:00 2001 From: ddc Date: Thu, 29 Jan 2026 14:44:44 -0300 Subject: [PATCH 1/9] V6.0.2 --- .github/workflows/workflow.yml | 7 +- pyproject.toml | 9 ++- uv.lock | 144 ++++++++++++++++++++++++++++++++- 3 files changed, 149 insertions(+), 11 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 80ff883..a3ebd2f 100755 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -9,15 +9,12 @@ name: CI/CD Pipeline jobs: test: name: Test Python ${{ matrix.python-version }} on ${{ matrix.os }} - runs-on: ${{ matrix.runs-on || matrix.os }} + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: ['ubuntu-latest', 'macos-latest', 'macos-14-arm64', 'windows-latest'] + os: ['ubuntu-latest', 'macos-latest', 'windows-latest'] python-version: ['3.12', '3.13', '3.14'] - include: - - os: 'macos-14-arm64' - runs-on: 'macos-14' defaults: run: working-directory: ${{ github.workspace }} diff --git a/pyproject.toml b/pyproject.toml index 5a24dca..f3cfcb1 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pythonLogs" -version = "6.0.1" +version = "6.0.2" description = "High-performance Python logging library with file rotation and optimized caching for better performance" license = {text = "MIT"} readme = "README.md" @@ -39,11 +39,16 @@ Repository = "https://github.com/ddc/pythonLogs" [project.optional-dependencies] test = [ - "poethepoet>=0.40.0", "psutil>=7.2.2", "pytest>=9.0.2", "pytest-cov>=7.0.0", ] +dev = [ + "pythonLogs[test]", + "black>=26.1.0", + "poethepoet>=0.40.0", + "ruff>=0.14.14", +] [tool.hatch.build] include = ["pythonLogs/**/*"] diff --git a/uv.lock b/uv.lock index 77ef293..ca752c1 100644 --- a/uv.lock +++ b/uv.lock @@ -11,6 +11,50 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, ] +[[package]] +name = "black" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "pytokens" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/13/88/560b11e521c522440af991d46848a2bde64b5f7202ec14e1f46f9509d328/black-26.1.0.tar.gz", hash = "sha256:d294ac3340eef9c9eb5d29288e96dc719ff269a88e27b396340459dd85da4c58", size = 658785, upload-time = "2026-01-18T04:50:11.993Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/13/710298938a61f0f54cdb4d1c0baeb672c01ff0358712eddaf29f76d32a0b/black-26.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6eeca41e70b5f5c84f2f913af857cf2ce17410847e1d54642e658e078da6544f", size = 1878189, upload-time = "2026-01-18T04:59:30.682Z" }, + { url = "https://files.pythonhosted.org/packages/79/a6/5179beaa57e5dbd2ec9f1c64016214057b4265647c62125aa6aeffb05392/black-26.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd39eef053e58e60204f2cdf059e2442e2eb08f15989eefe259870f89614c8b6", size = 1700178, upload-time = "2026-01-18T04:59:32.387Z" }, + { url = "https://files.pythonhosted.org/packages/8c/04/c96f79d7b93e8f09d9298b333ca0d31cd9b2ee6c46c274fd0f531de9dc61/black-26.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9459ad0d6cd483eacad4c6566b0f8e42af5e8b583cee917d90ffaa3778420a0a", size = 1777029, upload-time = "2026-01-18T04:59:33.767Z" }, + { url = "https://files.pythonhosted.org/packages/49/f9/71c161c4c7aa18bdda3776b66ac2dc07aed62053c7c0ff8bbda8c2624fe2/black-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a19915ec61f3a8746e8b10adbac4a577c6ba9851fa4a9e9fbfbcf319887a5791", size = 1406466, upload-time = "2026-01-18T04:59:35.177Z" }, + { url = "https://files.pythonhosted.org/packages/4a/8b/a7b0f974e473b159d0ac1b6bcefffeb6bec465898a516ee5cc989503cbc7/black-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:643d27fb5facc167c0b1b59d0315f2674a6e950341aed0fc05cf307d22bf4954", size = 1216393, upload-time = "2026-01-18T04:59:37.18Z" }, + { url = "https://files.pythonhosted.org/packages/79/04/fa2f4784f7237279332aa735cdfd5ae2e7730db0072fb2041dadda9ae551/black-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ba1d768fbfb6930fc93b0ecc32a43d8861ded16f47a40f14afa9bb04ab93d304", size = 1877781, upload-time = "2026-01-18T04:59:39.054Z" }, + { url = "https://files.pythonhosted.org/packages/cf/ad/5a131b01acc0e5336740a039628c0ab69d60cf09a2c87a4ec49f5826acda/black-26.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b807c240b64609cb0e80d2200a35b23c7df82259f80bef1b2c96eb422b4aac9", size = 1699670, upload-time = "2026-01-18T04:59:41.005Z" }, + { url = "https://files.pythonhosted.org/packages/da/7c/b05f22964316a52ab6b4265bcd52c0ad2c30d7ca6bd3d0637e438fc32d6e/black-26.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1de0f7d01cc894066a1153b738145b194414cc6eeaad8ef4397ac9abacf40f6b", size = 1775212, upload-time = "2026-01-18T04:59:42.545Z" }, + { url = "https://files.pythonhosted.org/packages/a6/a3/e8d1526bea0446e040193185353920a9506eab60a7d8beb062029129c7d2/black-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:91a68ae46bf07868963671e4d05611b179c2313301bd756a89ad4e3b3db2325b", size = 1409953, upload-time = "2026-01-18T04:59:44.357Z" }, + { url = "https://files.pythonhosted.org/packages/c7/5a/d62ebf4d8f5e3a1daa54adaab94c107b57be1b1a2f115a0249b41931e188/black-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:be5e2fe860b9bd9edbf676d5b60a9282994c03fbbd40fe8f5e75d194f96064ca", size = 1217707, upload-time = "2026-01-18T04:59:45.719Z" }, + { url = "https://files.pythonhosted.org/packages/6a/83/be35a175aacfce4b05584ac415fd317dd6c24e93a0af2dcedce0f686f5d8/black-26.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9dc8c71656a79ca49b8d3e2ce8103210c9481c57798b48deeb3a8bb02db5f115", size = 1871864, upload-time = "2026-01-18T04:59:47.586Z" }, + { url = "https://files.pythonhosted.org/packages/a5/f5/d33696c099450b1274d925a42b7a030cd3ea1f56d72e5ca8bbed5f52759c/black-26.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b22b3810451abe359a964cc88121d57f7bce482b53a066de0f1584988ca36e79", size = 1701009, upload-time = "2026-01-18T04:59:49.443Z" }, + { url = "https://files.pythonhosted.org/packages/1b/87/670dd888c537acb53a863bc15abbd85b22b429237d9de1b77c0ed6b79c42/black-26.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:53c62883b3f999f14e5d30b5a79bd437236658ad45b2f853906c7cbe79de00af", size = 1767806, upload-time = "2026-01-18T04:59:50.769Z" }, + { url = "https://files.pythonhosted.org/packages/fe/9c/cd3deb79bfec5bcf30f9d2100ffeec63eecce826eb63e3961708b9431ff1/black-26.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:f016baaadc423dc960cdddf9acae679e71ee02c4c341f78f3179d7e4819c095f", size = 1433217, upload-time = "2026-01-18T04:59:52.218Z" }, + { url = "https://files.pythonhosted.org/packages/4e/29/f3be41a1cf502a283506f40f5d27203249d181f7a1a2abce1c6ce188035a/black-26.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:66912475200b67ef5a0ab665011964bf924745103f51977a78b4fb92a9fc1bf0", size = 1245773, upload-time = "2026-01-18T04:59:54.457Z" }, + { url = "https://files.pythonhosted.org/packages/e4/3d/51bdb3ecbfadfaf825ec0c75e1de6077422b4afa2091c6c9ba34fbfc0c2d/black-26.1.0-py3-none-any.whl", hash = "sha256:1054e8e47ebd686e078c0bb0eaf31e6ce69c966058d122f2c0c950311f9f3ede", size = 204010, upload-time = "2026-01-18T04:50:09.978Z" }, +] + +[[package]] +name = "click" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, +] + [[package]] name = "colorama" version = "0.4.6" @@ -103,6 +147,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + [[package]] name = "packaging" version = "26.0" @@ -121,6 +174,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/aa/18/a8444036c6dd65ba3624c63b734d3ba95ba63ace513078e1580590075d21/pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364", size = 5955, upload-time = "2020-09-16T19:21:11.409Z" }, ] +[[package]] +name = "pathspec" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, +] + [[package]] name = "pluggy" version = "1.6.0" @@ -321,29 +392,68 @@ wheels = [ [[package]] name = "pythonlogs" -version = "6.0.1" +version = "6.0.2" source = { editable = "." } dependencies = [ { name = "pydantic-settings" }, ] [package.optional-dependencies] -test = [ +dev = [ + { name = "black" }, { name = "poethepoet" }, { name = "psutil" }, { name = "pytest" }, { name = "pytest-cov" }, + { name = "ruff" }, +] +test = [ + { name = "psutil" }, + { name = "pytest" }, + { name = "pytest-cov" }, ] [package.metadata] requires-dist = [ - { name = "poethepoet", marker = "extra == 'test'", specifier = ">=0.40.0" }, + { name = "black", marker = "extra == 'dev'", specifier = ">=26.1.0" }, + { name = "poethepoet", marker = "extra == 'dev'", specifier = ">=0.40.0" }, { name = "psutil", marker = "extra == 'test'", specifier = ">=7.2.2" }, { name = "pydantic-settings", specifier = ">=2.11.0" }, { name = "pytest", marker = "extra == 'test'", specifier = ">=9.0.2" }, { name = "pytest-cov", marker = "extra == 'test'", specifier = ">=7.0.0" }, + { name = "pythonlogs", extras = ["test"], marker = "extra == 'dev'" }, + { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.14.14" }, +] +provides-extras = ["test", "dev"] + +[[package]] +name = "pytokens" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/16/4b9cfd90d55e66ffdb277d7ebe3bc25250c2311336ec3fc73b2673c794d5/pytokens-0.4.0.tar.gz", hash = "sha256:6b0b03e6ea7c9f9d47c5c61164b69ad30f4f0d70a5d9fe7eac4d19f24f77af2d", size = 15039, upload-time = "2026-01-19T07:59:50.623Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/65/65460ebbfefd0bc1b160457904370d44f269e6e4582e0a9b6cba7c267b04/pytokens-0.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cd8da894e5a29ba6b6da8be06a4f7589d7220c099b5e363cb0643234b9b38c2a", size = 159864, upload-time = "2026-01-19T07:59:08.908Z" }, + { url = "https://files.pythonhosted.org/packages/25/70/a46669ec55876c392036b4da9808b5c3b1c5870bbca3d4cc923bf68bdbc1/pytokens-0.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:237ba7cfb677dbd3b01b09860810aceb448871150566b93cd24501d5734a04b1", size = 254448, upload-time = "2026-01-19T07:59:10.594Z" }, + { url = "https://files.pythonhosted.org/packages/62/0b/c486fc61299c2fc3b7f88ee4e115d4c8b6ffd1a7f88dc94b398b5b1bc4b8/pytokens-0.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01d1a61e36812e4e971cfe2c0e4c1f2d66d8311031dac8bf168af8a249fa04dd", size = 268863, upload-time = "2026-01-19T07:59:12.31Z" }, + { url = "https://files.pythonhosted.org/packages/79/92/b036af846707d25feaff7cafbd5280f1bd6a1034c16bb06a7c910209c1ab/pytokens-0.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e47e2ef3ec6ee86909e520d79f965f9b23389fda47460303cf715d510a6fe544", size = 267181, upload-time = "2026-01-19T07:59:13.856Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c0/6d011fc00fefa74ce34816c84a923d2dd7c46b8dbc6ee52d13419786834c/pytokens-0.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:3d36954aba4557fd5a418a03cf595ecbb1cdcce119f91a49b19ef09d691a22ae", size = 102814, upload-time = "2026-01-19T07:59:15.288Z" }, + { url = "https://files.pythonhosted.org/packages/98/63/627b7e71d557383da5a97f473ad50f8d9c2c1f55c7d3c2531a120c796f6e/pytokens-0.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73eff3bdd8ad08da679867992782568db0529b887bed4c85694f84cdf35eafc6", size = 159744, upload-time = "2026-01-19T07:59:16.88Z" }, + { url = "https://files.pythonhosted.org/packages/28/d7/16f434c37ec3824eba6bcb6e798e5381a8dc83af7a1eda0f95c16fe3ade5/pytokens-0.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d97cc1f91b1a8e8ebccf31c367f28225699bea26592df27141deade771ed0afb", size = 253207, upload-time = "2026-01-19T07:59:18.069Z" }, + { url = "https://files.pythonhosted.org/packages/ab/96/04102856b9527701ae57d74a6393d1aca5bad18a1b1ca48ccffb3c93b392/pytokens-0.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c8952c537cb73a1a74369501a83b7f9d208c3cf92c41dd88a17814e68d48ce", size = 267452, upload-time = "2026-01-19T07:59:19.328Z" }, + { url = "https://files.pythonhosted.org/packages/0e/ef/0936eb472b89ab2d2c2c24bb81c50417e803fa89c731930d9fb01176fe9f/pytokens-0.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5dbf56f3c748aed9310b310d5b8b14e2c96d3ad682ad5a943f381bdbbdddf753", size = 265965, upload-time = "2026-01-19T07:59:20.613Z" }, + { url = "https://files.pythonhosted.org/packages/ae/f5/64f3d6f7df4a9e92ebda35ee85061f6260e16eac82df9396020eebbca775/pytokens-0.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:e131804513597f2dff2b18f9911d9b6276e21ef3699abeffc1c087c65a3d975e", size = 102813, upload-time = "2026-01-19T07:59:22.012Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f1/d07e6209f18ef378fc2ae9dee8d1dfe91fd2447c2e2dbfa32867b6dd30cf/pytokens-0.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0d7374c917197106d3c4761374718bc55ea2e9ac0fb94171588ef5840ee1f016", size = 159968, upload-time = "2026-01-19T07:59:23.07Z" }, + { url = "https://files.pythonhosted.org/packages/0a/73/0eb111400abd382a04f253b269819db9fcc748aa40748441cebdcb6d068f/pytokens-0.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cd3fa1caf9e47a72ee134a29ca6b5bea84712724bba165d6628baa190c6ea5b", size = 253373, upload-time = "2026-01-19T07:59:24.381Z" }, + { url = "https://files.pythonhosted.org/packages/bd/8d/9e4e2fdb5bcaba679e54afcc304e9f13f488eb4d626e6b613f9553e03dbd/pytokens-0.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c6986576b7b07fe9791854caa5347923005a80b079d45b63b0be70d50cce5f1", size = 267024, upload-time = "2026-01-19T07:59:25.74Z" }, + { url = "https://files.pythonhosted.org/packages/cb/b7/e0a370321af2deb772cff14ff337e1140d1eac2c29a8876bfee995f486f0/pytokens-0.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9940f7c2e2f54fb1cb5fe17d0803c54da7a2bf62222704eb4217433664a186a7", size = 270912, upload-time = "2026-01-19T07:59:27.072Z" }, + { url = "https://files.pythonhosted.org/packages/7c/54/4348f916c440d4c3e68b53b4ed0e66b292d119e799fa07afa159566dcc86/pytokens-0.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:54691cf8f299e7efabcc25adb4ce715d3cef1491e1c930eaf555182f898ef66a", size = 103836, upload-time = "2026-01-19T07:59:28.112Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f8/a693c0cfa9c783a2a8c4500b7b2a8bab420f8ca4f2d496153226bf1c12e3/pytokens-0.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:94ff5db97a0d3cd7248a5b07ba2167bd3edc1db92f76c6db00137bbaf068ddf8", size = 167643, upload-time = "2026-01-19T07:59:29.292Z" }, + { url = "https://files.pythonhosted.org/packages/c0/dd/a64eb1e9f3ec277b69b33ef1b40ffbcc8f0a3bafcde120997efc7bdefebf/pytokens-0.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d0dd6261cd9cc95fae1227b1b6ebee023a5fd4a4b6330b071c73a516f5f59b63", size = 289553, upload-time = "2026-01-19T07:59:30.537Z" }, + { url = "https://files.pythonhosted.org/packages/df/22/06c1079d93dbc3bca5d013e1795f3d8b9ed6c87290acd6913c1c526a6bb2/pytokens-0.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cdca8159df407dbd669145af4171a0d967006e0be25f3b520896bc7068f02c4", size = 302490, upload-time = "2026-01-19T07:59:32.352Z" }, + { url = "https://files.pythonhosted.org/packages/8d/de/a6f5e43115b4fbf4b93aa87d6c83c79932cdb084f9711daae04549e1e4ad/pytokens-0.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4b5770abeb2a24347380a1164a558f0ebe06e98aedbd54c45f7929527a5fb26e", size = 305652, upload-time = "2026-01-19T07:59:33.685Z" }, + { url = "https://files.pythonhosted.org/packages/ab/3d/c136e057cb622e36e0c3ff7a8aaa19ff9720050c4078235691da885fe6ee/pytokens-0.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:74500d72c561dad14c037a9e86a657afd63e277dd5a3bb7570932ab7a3b12551", size = 115472, upload-time = "2026-01-19T07:59:34.734Z" }, + { url = "https://files.pythonhosted.org/packages/7c/3c/6941a82f4f130af6e1c68c076b6789069ef10c04559bd4733650f902fd3b/pytokens-0.4.0-py3-none-any.whl", hash = "sha256:0508d11b4de157ee12063901603be87fb0253e8f4cb9305eb168b1202ab92068", size = 13224, upload-time = "2026-01-19T07:59:49.822Z" }, ] -provides-extras = ["test"] [[package]] name = "pyyaml" @@ -391,6 +501,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] +[[package]] +name = "ruff" +version = "0.14.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/06/f71e3a86b2df0dfa2d2f72195941cd09b44f87711cb7fa5193732cb9a5fc/ruff-0.14.14.tar.gz", hash = "sha256:2d0f819c9a90205f3a867dbbd0be083bee9912e170fd7d9704cc8ae45824896b", size = 4515732, upload-time = "2026-01-22T22:30:17.527Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/89/20a12e97bc6b9f9f68343952da08a8099c57237aef953a56b82711d55edd/ruff-0.14.14-py3-none-linux_armv6l.whl", hash = "sha256:7cfe36b56e8489dee8fbc777c61959f60ec0f1f11817e8f2415f429552846aed", size = 10467650, upload-time = "2026-01-22T22:30:08.578Z" }, + { url = "https://files.pythonhosted.org/packages/a3/b1/c5de3fd2d5a831fcae21beda5e3589c0ba67eec8202e992388e4b17a6040/ruff-0.14.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6006a0082336e7920b9573ef8a7f52eec837add1265cc74e04ea8a4368cd704c", size = 10883245, upload-time = "2026-01-22T22:30:04.155Z" }, + { url = "https://files.pythonhosted.org/packages/b8/7c/3c1db59a10e7490f8f6f8559d1db8636cbb13dccebf18686f4e3c9d7c772/ruff-0.14.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:026c1d25996818f0bf498636686199d9bd0d9d6341c9c2c3b62e2a0198b758de", size = 10231273, upload-time = "2026-01-22T22:30:34.642Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6e/5e0e0d9674be0f8581d1f5e0f0a04761203affce3232c1a1189d0e3b4dad/ruff-0.14.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f666445819d31210b71e0a6d1c01e24447a20b85458eea25a25fe8142210ae0e", size = 10585753, upload-time = "2026-01-22T22:30:31.781Z" }, + { url = "https://files.pythonhosted.org/packages/23/09/754ab09f46ff1884d422dc26d59ba18b4e5d355be147721bb2518aa2a014/ruff-0.14.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c0f18b922c6d2ff9a5e6c3ee16259adc513ca775bcf82c67ebab7cbd9da5bc8", size = 10286052, upload-time = "2026-01-22T22:30:24.827Z" }, + { url = "https://files.pythonhosted.org/packages/c8/cc/e71f88dd2a12afb5f50733851729d6b571a7c3a35bfdb16c3035132675a0/ruff-0.14.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1629e67489c2dea43e8658c3dba659edbfd87361624b4040d1df04c9740ae906", size = 11043637, upload-time = "2026-01-22T22:30:13.239Z" }, + { url = "https://files.pythonhosted.org/packages/67/b2/397245026352494497dac935d7f00f1468c03a23a0c5db6ad8fc49ca3fb2/ruff-0.14.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:27493a2131ea0f899057d49d303e4292b2cae2bb57253c1ed1f256fbcd1da480", size = 12194761, upload-time = "2026-01-22T22:30:22.542Z" }, + { url = "https://files.pythonhosted.org/packages/5b/06/06ef271459f778323112c51b7587ce85230785cd64e91772034ddb88f200/ruff-0.14.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01ff589aab3f5b539e35db38425da31a57521efd1e4ad1ae08fc34dbe30bd7df", size = 12005701, upload-time = "2026-01-22T22:30:20.499Z" }, + { url = "https://files.pythonhosted.org/packages/41/d6/99364514541cf811ccc5ac44362f88df66373e9fec1b9d1c4cc830593fe7/ruff-0.14.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc12d74eef0f29f51775f5b755913eb523546b88e2d733e1d701fe65144e89b", size = 11282455, upload-time = "2026-01-22T22:29:59.679Z" }, + { url = "https://files.pythonhosted.org/packages/ca/71/37daa46f89475f8582b7762ecd2722492df26421714a33e72ccc9a84d7a5/ruff-0.14.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb8481604b7a9e75eff53772496201690ce2687067e038b3cc31aaf16aa0b974", size = 11215882, upload-time = "2026-01-22T22:29:57.032Z" }, + { url = "https://files.pythonhosted.org/packages/2c/10/a31f86169ec91c0705e618443ee74ede0bdd94da0a57b28e72db68b2dbac/ruff-0.14.14-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:14649acb1cf7b5d2d283ebd2f58d56b75836ed8c6f329664fa91cdea19e76e66", size = 11180549, upload-time = "2026-01-22T22:30:27.175Z" }, + { url = "https://files.pythonhosted.org/packages/fd/1e/c723f20536b5163adf79bdd10c5f093414293cdf567eed9bdb7b83940f3f/ruff-0.14.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e8058d2145566510790eab4e2fad186002e288dec5e0d343a92fe7b0bc1b3e13", size = 10543416, upload-time = "2026-01-22T22:30:01.964Z" }, + { url = "https://files.pythonhosted.org/packages/3e/34/8a84cea7e42c2d94ba5bde1d7a4fae164d6318f13f933d92da6d7c2041ff/ruff-0.14.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e651e977a79e4c758eb807f0481d673a67ffe53cfa92209781dfa3a996cf8412", size = 10285491, upload-time = "2026-01-22T22:30:29.51Z" }, + { url = "https://files.pythonhosted.org/packages/55/ef/b7c5ea0be82518906c978e365e56a77f8de7678c8bb6651ccfbdc178c29f/ruff-0.14.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cc8b22da8d9d6fdd844a68ae937e2a0adf9b16514e9a97cc60355e2d4b219fc3", size = 10733525, upload-time = "2026-01-22T22:30:06.499Z" }, + { url = "https://files.pythonhosted.org/packages/6a/5b/aaf1dfbcc53a2811f6cc0a1759de24e4b03e02ba8762daabd9b6bd8c59e3/ruff-0.14.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:16bc890fb4cc9781bb05beb5ab4cd51be9e7cb376bf1dd3580512b24eb3fda2b", size = 11315626, upload-time = "2026-01-22T22:30:36.848Z" }, + { url = "https://files.pythonhosted.org/packages/2c/aa/9f89c719c467dfaf8ad799b9bae0df494513fb21d31a6059cb5870e57e74/ruff-0.14.14-py3-none-win32.whl", hash = "sha256:b530c191970b143375b6a68e6f743800b2b786bbcf03a7965b06c4bf04568167", size = 10502442, upload-time = "2026-01-22T22:30:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/87/44/90fa543014c45560cae1fffc63ea059fb3575ee6e1cb654562197e5d16fb/ruff-0.14.14-py3-none-win_amd64.whl", hash = "sha256:3dde1435e6b6fe5b66506c1dff67a421d0b7f6488d466f651c07f4cab3bf20fd", size = 11630486, upload-time = "2026-01-22T22:30:10.852Z" }, + { url = "https://files.pythonhosted.org/packages/9e/6a/40fee331a52339926a92e17ae748827270b288a35ef4a15c9c8f2ec54715/ruff-0.14.14-py3-none-win_arm64.whl", hash = "sha256:56e6981a98b13a32236a72a8da421d7839221fa308b223b9283312312e5ac76c", size = 10920448, upload-time = "2026-01-22T22:30:15.417Z" }, +] + [[package]] name = "typing-extensions" version = "4.15.0" From 96094e96390ace3d469bc26c216d4e177310c6e8 Mon Sep 17 00:00:00 2001 From: ddc Date: Mon, 2 Feb 2026 17:15:42 -0300 Subject: [PATCH 2/9] V6.0.2 --- .github/workflows/workflow.yml | 2 +- pyproject.toml | 8 ++++---- uv.lock | 29 +++++++++++++++-------------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index a3ebd2f..a51c4f5 100755 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -28,7 +28,7 @@ jobs: run: uv python install ${{ matrix.python-version }} - name: Install dependencies - run: uv sync --all-extras + run: uv sync --group test shell: bash - name: Run tests with coverage diff --git a/pyproject.toml b/pyproject.toml index f3cfcb1..1cfc712 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,14 +37,14 @@ dependencies = [ Homepage = "https://pypi.org/project/pythonLogs" Repository = "https://github.com/ddc/pythonLogs" -[project.optional-dependencies] +[dependency-groups] test = [ "psutil>=7.2.2", - "pytest>=9.0.2", + "pytest-cov>=7.0.0", "pytest-cov>=7.0.0", ] dev = [ - "pythonLogs[test]", + {include-group = "test"}, "black>=26.1.0", "poethepoet>=0.40.0", "ruff>=0.14.14", @@ -58,7 +58,7 @@ packages = ["pythonLogs"] [tool.poe.tasks] build = "uv build --wheel" -updatedev.shell = "uv lock && uv sync --no-install-project --all-extras" +updatedev.shell = "uv lock && uv sync --no-install-project --all-groups" linter.shell = "uv run ruff check --fix . && uv run black ." profile = "uv run python -m cProfile -o cprofile.prof -m pytest" test = "uv run pytest" diff --git a/uv.lock b/uv.lock index ca752c1..b5bf134 100644 --- a/uv.lock +++ b/uv.lock @@ -398,33 +398,34 @@ dependencies = [ { name = "pydantic-settings" }, ] -[package.optional-dependencies] +[package.dev-dependencies] dev = [ { name = "black" }, { name = "poethepoet" }, { name = "psutil" }, - { name = "pytest" }, { name = "pytest-cov" }, { name = "ruff" }, ] test = [ { name = "psutil" }, - { name = "pytest" }, { name = "pytest-cov" }, ] [package.metadata] -requires-dist = [ - { name = "black", marker = "extra == 'dev'", specifier = ">=26.1.0" }, - { name = "poethepoet", marker = "extra == 'dev'", specifier = ">=0.40.0" }, - { name = "psutil", marker = "extra == 'test'", specifier = ">=7.2.2" }, - { name = "pydantic-settings", specifier = ">=2.11.0" }, - { name = "pytest", marker = "extra == 'test'", specifier = ">=9.0.2" }, - { name = "pytest-cov", marker = "extra == 'test'", specifier = ">=7.0.0" }, - { name = "pythonlogs", extras = ["test"], marker = "extra == 'dev'" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.14.14" }, -] -provides-extras = ["test", "dev"] +requires-dist = [{ name = "pydantic-settings", specifier = ">=2.11.0" }] + +[package.metadata.requires-dev] +dev = [ + { name = "black", specifier = ">=26.1.0" }, + { name = "poethepoet", specifier = ">=0.40.0" }, + { name = "psutil", specifier = ">=7.2.2" }, + { name = "pytest-cov", specifier = ">=7.0.0" }, + { name = "ruff", specifier = ">=0.14.14" }, +] +test = [ + { name = "psutil", specifier = ">=7.2.2" }, + { name = "pytest-cov", specifier = ">=7.0.0" }, +] [[package]] name = "pytokens" From caf0c443928afe48b1ce1e7495e9f09f4d032536 Mon Sep 17 00:00:00 2001 From: ddc Date: Tue, 3 Feb 2026 17:56:18 -0300 Subject: [PATCH 3/9] V6.0.2 --- pyproject.toml | 2 +- uv.lock | 223 ++++++++++++++++++++++++------------------------- 2 files changed, 112 insertions(+), 113 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1cfc712..8e59784 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,7 @@ dev = [ {include-group = "test"}, "black>=26.1.0", "poethepoet>=0.40.0", - "ruff>=0.14.14", + "ruff>=0.15.0", ] [tool.hatch.build] diff --git a/uv.lock b/uv.lock index b5bf134..f0d2350 100644 --- a/uv.lock +++ b/uv.lock @@ -66,76 +66,76 @@ wheels = [ [[package]] name = "coverage" -version = "7.13.2" +version = "7.13.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ad/49/349848445b0e53660e258acbcc9b0d014895b6739237920886672240f84b/coverage-7.13.2.tar.gz", hash = "sha256:044c6951ec37146b72a50cc81ef02217d27d4c3640efd2640311393cbbf143d3", size = 826523, upload-time = "2026-01-25T13:00:04.889Z" } +sdist = { url = "https://files.pythonhosted.org/packages/11/43/3e4ac666cc35f231fa70c94e9f38459299de1a152813f9d2f60fc5f3ecaf/coverage-7.13.3.tar.gz", hash = "sha256:f7f6182d3dfb8802c1747eacbfe611b669455b69b7c037484bb1efbbb56711ac", size = 826832, upload-time = "2026-02-03T14:02:30.944Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/39/e92a35f7800222d3f7b2cbb7bbc3b65672ae8d501cb31801b2d2bd7acdf1/coverage-7.13.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f106b2af193f965d0d3234f3f83fc35278c7fb935dfbde56ae2da3dd2c03b84d", size = 219142, upload-time = "2026-01-25T12:58:00.448Z" }, - { url = "https://files.pythonhosted.org/packages/45/7a/8bf9e9309c4c996e65c52a7c5a112707ecdd9fbaf49e10b5a705a402bbb4/coverage-7.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f45d21dc4d5d6bd29323f0320089ef7eae16e4bef712dff79d184fa7330af3", size = 219503, upload-time = "2026-01-25T12:58:02.451Z" }, - { url = "https://files.pythonhosted.org/packages/87/93/17661e06b7b37580923f3f12406ac91d78aeed293fb6da0b69cc7957582f/coverage-7.13.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fae91dfecd816444c74531a9c3d6ded17a504767e97aa674d44f638107265b99", size = 251006, upload-time = "2026-01-25T12:58:04.059Z" }, - { url = "https://files.pythonhosted.org/packages/12/f0/f9e59fb8c310171497f379e25db060abef9fa605e09d63157eebec102676/coverage-7.13.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:264657171406c114787b441484de620e03d8f7202f113d62fcd3d9688baa3e6f", size = 253750, upload-time = "2026-01-25T12:58:05.574Z" }, - { url = "https://files.pythonhosted.org/packages/e5/b1/1935e31add2232663cf7edd8269548b122a7d100047ff93475dbaaae673e/coverage-7.13.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae47d8dcd3ded0155afbb59c62bd8ab07ea0fd4902e1c40567439e6db9dcaf2f", size = 254862, upload-time = "2026-01-25T12:58:07.647Z" }, - { url = "https://files.pythonhosted.org/packages/af/59/b5e97071ec13df5f45da2b3391b6cdbec78ba20757bc92580a5b3d5fa53c/coverage-7.13.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8a0b33e9fd838220b007ce8f299114d406c1e8edb21336af4c97a26ecfd185aa", size = 251420, upload-time = "2026-01-25T12:58:09.309Z" }, - { url = "https://files.pythonhosted.org/packages/3f/75/9495932f87469d013dc515fb0ce1aac5fa97766f38f6b1a1deb1ee7b7f3a/coverage-7.13.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b3becbea7f3ce9a2d4d430f223ec15888e4deb31395840a79e916368d6004cce", size = 252786, upload-time = "2026-01-25T12:58:10.909Z" }, - { url = "https://files.pythonhosted.org/packages/6a/59/af550721f0eb62f46f7b8cb7e6f1860592189267b1c411a4e3a057caacee/coverage-7.13.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f819c727a6e6eeb8711e4ce63d78c620f69630a2e9d53bc95ca5379f57b6ba94", size = 250928, upload-time = "2026-01-25T12:58:12.449Z" }, - { url = "https://files.pythonhosted.org/packages/9b/b1/21b4445709aae500be4ab43bbcfb4e53dc0811c3396dcb11bf9f23fd0226/coverage-7.13.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:4f7b71757a3ab19f7ba286e04c181004c1d61be921795ee8ba6970fd0ec91da5", size = 250496, upload-time = "2026-01-25T12:58:14.047Z" }, - { url = "https://files.pythonhosted.org/packages/ba/b1/0f5d89dfe0392990e4f3980adbde3eb34885bc1effb2dc369e0bf385e389/coverage-7.13.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b7fc50d2afd2e6b4f6f2f403b70103d280a8e0cb35320cbbe6debcda02a1030b", size = 252373, upload-time = "2026-01-25T12:58:15.976Z" }, - { url = "https://files.pythonhosted.org/packages/01/c9/0cf1a6a57a9968cc049a6b896693faa523c638a5314b1fc374eb2b2ac904/coverage-7.13.2-cp312-cp312-win32.whl", hash = "sha256:292250282cf9bcf206b543d7608bda17ca6fc151f4cbae949fc7e115112fbd41", size = 221696, upload-time = "2026-01-25T12:58:17.517Z" }, - { url = "https://files.pythonhosted.org/packages/4d/05/d7540bf983f09d32803911afed135524570f8c47bb394bf6206c1dc3a786/coverage-7.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:eeea10169fac01549a7921d27a3e517194ae254b542102267bef7a93ed38c40e", size = 222504, upload-time = "2026-01-25T12:58:19.115Z" }, - { url = "https://files.pythonhosted.org/packages/15/8b/1a9f037a736ced0a12aacf6330cdaad5008081142a7070bc58b0f7930cbc/coverage-7.13.2-cp312-cp312-win_arm64.whl", hash = "sha256:2a5b567f0b635b592c917f96b9a9cb3dbd4c320d03f4bf94e9084e494f2e8894", size = 221120, upload-time = "2026-01-25T12:58:21.334Z" }, - { url = "https://files.pythonhosted.org/packages/a7/f0/3d3eac7568ab6096ff23791a526b0048a1ff3f49d0e236b2af6fb6558e88/coverage-7.13.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ed75de7d1217cf3b99365d110975f83af0528c849ef5180a12fd91b5064df9d6", size = 219168, upload-time = "2026-01-25T12:58:23.376Z" }, - { url = "https://files.pythonhosted.org/packages/a3/a6/f8b5cfeddbab95fdef4dcd682d82e5dcff7a112ced57a959f89537ee9995/coverage-7.13.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97e596de8fa9bada4d88fde64a3f4d37f1b6131e4faa32bad7808abc79887ddc", size = 219537, upload-time = "2026-01-25T12:58:24.932Z" }, - { url = "https://files.pythonhosted.org/packages/7b/e6/8d8e6e0c516c838229d1e41cadcec91745f4b1031d4db17ce0043a0423b4/coverage-7.13.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:68c86173562ed4413345410c9480a8d64864ac5e54a5cda236748031e094229f", size = 250528, upload-time = "2026-01-25T12:58:26.567Z" }, - { url = "https://files.pythonhosted.org/packages/8e/78/befa6640f74092b86961f957f26504c8fba3d7da57cc2ab7407391870495/coverage-7.13.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7be4d613638d678b2b3773b8f687537b284d7074695a43fe2fbbfc0e31ceaed1", size = 253132, upload-time = "2026-01-25T12:58:28.251Z" }, - { url = "https://files.pythonhosted.org/packages/9d/10/1630db1edd8ce675124a2ee0f7becc603d2bb7b345c2387b4b95c6907094/coverage-7.13.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7f63ce526a96acd0e16c4af8b50b64334239550402fb1607ce6a584a6d62ce9", size = 254374, upload-time = "2026-01-25T12:58:30.294Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1d/0d9381647b1e8e6d310ac4140be9c428a0277330991e0c35bdd751e338a4/coverage-7.13.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:406821f37f864f968e29ac14c3fccae0fec9fdeba48327f0341decf4daf92d7c", size = 250762, upload-time = "2026-01-25T12:58:32.036Z" }, - { url = "https://files.pythonhosted.org/packages/43/e4/5636dfc9a7c871ee8776af83ee33b4c26bc508ad6cee1e89b6419a366582/coverage-7.13.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ee68e5a4e3e5443623406b905db447dceddffee0dceb39f4e0cd9ec2a35004b5", size = 252502, upload-time = "2026-01-25T12:58:33.961Z" }, - { url = "https://files.pythonhosted.org/packages/02/2a/7ff2884d79d420cbb2d12fed6fff727b6d0ef27253140d3cdbbd03187ee0/coverage-7.13.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2ee0e58cca0c17dd9c6c1cdde02bb705c7b3fbfa5f3b0b5afeda20d4ebff8ef4", size = 250463, upload-time = "2026-01-25T12:58:35.529Z" }, - { url = "https://files.pythonhosted.org/packages/91/c0/ba51087db645b6c7261570400fc62c89a16278763f36ba618dc8657a187b/coverage-7.13.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6e5bbb5018bf76a56aabdb64246b5288d5ae1b7d0dd4d0534fe86df2c2992d1c", size = 250288, upload-time = "2026-01-25T12:58:37.226Z" }, - { url = "https://files.pythonhosted.org/packages/03/07/44e6f428551c4d9faf63ebcefe49b30e5c89d1be96f6a3abd86a52da9d15/coverage-7.13.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a55516c68ef3e08e134e818d5e308ffa6b1337cc8b092b69b24287bf07d38e31", size = 252063, upload-time = "2026-01-25T12:58:38.821Z" }, - { url = "https://files.pythonhosted.org/packages/c2/67/35b730ad7e1859dd57e834d1bc06080d22d2f87457d53f692fce3f24a5a9/coverage-7.13.2-cp313-cp313-win32.whl", hash = "sha256:5b20211c47a8abf4abc3319d8ce2464864fa9f30c5fcaf958a3eed92f4f1fef8", size = 221716, upload-time = "2026-01-25T12:58:40.484Z" }, - { url = "https://files.pythonhosted.org/packages/0d/82/e5fcf5a97c72f45fc14829237a6550bf49d0ab882ac90e04b12a69db76b4/coverage-7.13.2-cp313-cp313-win_amd64.whl", hash = "sha256:14f500232e521201cf031549fb1ebdfc0a40f401cf519157f76c397e586c3beb", size = 222522, upload-time = "2026-01-25T12:58:43.247Z" }, - { url = "https://files.pythonhosted.org/packages/b1/f1/25d7b2f946d239dd2d6644ca2cc060d24f97551e2af13b6c24c722ae5f97/coverage-7.13.2-cp313-cp313-win_arm64.whl", hash = "sha256:9779310cb5a9778a60c899f075a8514c89fa6d10131445c2207fc893e0b14557", size = 221145, upload-time = "2026-01-25T12:58:45Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f7/080376c029c8f76fadfe43911d0daffa0cbdc9f9418a0eead70c56fb7f4b/coverage-7.13.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e64fa5a1e41ce5df6b547cbc3d3699381c9e2c2c369c67837e716ed0f549d48e", size = 219861, upload-time = "2026-01-25T12:58:46.586Z" }, - { url = "https://files.pythonhosted.org/packages/42/11/0b5e315af5ab35f4c4a70e64d3314e4eec25eefc6dec13be3a7d5ffe8ac5/coverage-7.13.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b01899e82a04085b6561eb233fd688474f57455e8ad35cd82286463ba06332b7", size = 220207, upload-time = "2026-01-25T12:58:48.277Z" }, - { url = "https://files.pythonhosted.org/packages/b2/0c/0874d0318fb1062117acbef06a09cf8b63f3060c22265adaad24b36306b7/coverage-7.13.2-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:838943bea48be0e2768b0cf7819544cdedc1bbb2f28427eabb6eb8c9eb2285d3", size = 261504, upload-time = "2026-01-25T12:58:49.904Z" }, - { url = "https://files.pythonhosted.org/packages/83/5e/1cd72c22ecb30751e43a72f40ba50fcef1b7e93e3ea823bd9feda8e51f9a/coverage-7.13.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:93d1d25ec2b27e90bcfef7012992d1f5121b51161b8bffcda756a816cf13c2c3", size = 263582, upload-time = "2026-01-25T12:58:51.582Z" }, - { url = "https://files.pythonhosted.org/packages/9b/da/8acf356707c7a42df4d0657020308e23e5a07397e81492640c186268497c/coverage-7.13.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93b57142f9621b0d12349c43fc7741fe578e4bc914c1e5a54142856cfc0bf421", size = 266008, upload-time = "2026-01-25T12:58:53.234Z" }, - { url = "https://files.pythonhosted.org/packages/41/41/ea1730af99960309423c6ea8d6a4f1fa5564b2d97bd1d29dda4b42611f04/coverage-7.13.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f06799ae1bdfff7ccb8665d75f8291c69110ba9585253de254688aa8a1ccc6c5", size = 260762, upload-time = "2026-01-25T12:58:55.372Z" }, - { url = "https://files.pythonhosted.org/packages/22/fa/02884d2080ba71db64fdc127b311db60e01fe6ba797d9c8363725e39f4d5/coverage-7.13.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7f9405ab4f81d490811b1d91c7a20361135a2df4c170e7f0b747a794da5b7f23", size = 263571, upload-time = "2026-01-25T12:58:57.52Z" }, - { url = "https://files.pythonhosted.org/packages/d2/6b/4083aaaeba9b3112f55ac57c2ce7001dc4d8fa3fcc228a39f09cc84ede27/coverage-7.13.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f9ab1d5b86f8fbc97a5b3cd6280a3fd85fef3b028689d8a2c00918f0d82c728c", size = 261200, upload-time = "2026-01-25T12:58:59.255Z" }, - { url = "https://files.pythonhosted.org/packages/e9/d2/aea92fa36d61955e8c416ede9cf9bf142aa196f3aea214bb67f85235a050/coverage-7.13.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:f674f59712d67e841525b99e5e2b595250e39b529c3bda14764e4f625a3fa01f", size = 260095, upload-time = "2026-01-25T12:59:01.066Z" }, - { url = "https://files.pythonhosted.org/packages/0d/ae/04ffe96a80f107ea21b22b2367175c621da920063260a1c22f9452fd7866/coverage-7.13.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c6cadac7b8ace1ba9144feb1ae3cb787a6065ba6d23ffc59a934b16406c26573", size = 262284, upload-time = "2026-01-25T12:59:02.802Z" }, - { url = "https://files.pythonhosted.org/packages/1c/7a/6f354dcd7dfc41297791d6fb4e0d618acb55810bde2c1fd14b3939e05c2b/coverage-7.13.2-cp313-cp313t-win32.whl", hash = "sha256:14ae4146465f8e6e6253eba0cccd57423e598a4cb925958b240c805300918343", size = 222389, upload-time = "2026-01-25T12:59:04.563Z" }, - { url = "https://files.pythonhosted.org/packages/8d/d5/080ad292a4a3d3daf411574be0a1f56d6dee2c4fdf6b005342be9fac807f/coverage-7.13.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9074896edd705a05769e3de0eac0a8388484b503b68863dd06d5e473f874fd47", size = 223450, upload-time = "2026-01-25T12:59:06.677Z" }, - { url = "https://files.pythonhosted.org/packages/88/96/df576fbacc522e9fb8d1c4b7a7fc62eb734be56e2cba1d88d2eabe08ea3f/coverage-7.13.2-cp313-cp313t-win_arm64.whl", hash = "sha256:69e526e14f3f854eda573d3cf40cffd29a1a91c684743d904c33dbdcd0e0f3e7", size = 221707, upload-time = "2026-01-25T12:59:08.363Z" }, - { url = "https://files.pythonhosted.org/packages/55/53/1da9e51a0775634b04fcc11eb25c002fc58ee4f92ce2e8512f94ac5fc5bf/coverage-7.13.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:387a825f43d680e7310e6f325b2167dd093bc8ffd933b83e9aa0983cf6e0a2ef", size = 219213, upload-time = "2026-01-25T12:59:11.909Z" }, - { url = "https://files.pythonhosted.org/packages/46/35/b3caac3ebbd10230fea5a33012b27d19e999a17c9285c4228b4b2e35b7da/coverage-7.13.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f0d7fea9d8e5d778cd5a9e8fc38308ad688f02040e883cdc13311ef2748cb40f", size = 219549, upload-time = "2026-01-25T12:59:13.638Z" }, - { url = "https://files.pythonhosted.org/packages/76/9c/e1cf7def1bdc72c1907e60703983a588f9558434a2ff94615747bd73c192/coverage-7.13.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e080afb413be106c95c4ee96b4fffdc9e2fa56a8bbf90b5c0918e5c4449412f5", size = 250586, upload-time = "2026-01-25T12:59:15.808Z" }, - { url = "https://files.pythonhosted.org/packages/ba/49/f54ec02ed12be66c8d8897270505759e057b0c68564a65c429ccdd1f139e/coverage-7.13.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a7fc042ba3c7ce25b8a9f097eb0f32a5ce1ccdb639d9eec114e26def98e1f8a4", size = 253093, upload-time = "2026-01-25T12:59:17.491Z" }, - { url = "https://files.pythonhosted.org/packages/fb/5e/aaf86be3e181d907e23c0f61fccaeb38de8e6f6b47aed92bf57d8fc9c034/coverage-7.13.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d0ba505e021557f7f8173ee8cd6b926373d8653e5ff7581ae2efce1b11ef4c27", size = 254446, upload-time = "2026-01-25T12:59:19.752Z" }, - { url = "https://files.pythonhosted.org/packages/28/c8/a5fa01460e2d75b0c853b392080d6829d3ca8b5ab31e158fa0501bc7c708/coverage-7.13.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7de326f80e3451bd5cc7239ab46c73ddb658fe0b7649476bc7413572d36cd548", size = 250615, upload-time = "2026-01-25T12:59:21.928Z" }, - { url = "https://files.pythonhosted.org/packages/86/0b/6d56315a55f7062bb66410732c24879ccb2ec527ab6630246de5fe45a1df/coverage-7.13.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:abaea04f1e7e34841d4a7b343904a3f59481f62f9df39e2cd399d69a187a9660", size = 252452, upload-time = "2026-01-25T12:59:23.592Z" }, - { url = "https://files.pythonhosted.org/packages/30/19/9bc550363ebc6b0ea121977ee44d05ecd1e8bf79018b8444f1028701c563/coverage-7.13.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9f93959ee0c604bccd8e0697be21de0887b1f73efcc3aa73a3ec0fd13feace92", size = 250418, upload-time = "2026-01-25T12:59:25.392Z" }, - { url = "https://files.pythonhosted.org/packages/1f/53/580530a31ca2f0cc6f07a8f2ab5460785b02bb11bdf815d4c4d37a4c5169/coverage-7.13.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:13fe81ead04e34e105bf1b3c9f9cdf32ce31736ee5d90a8d2de02b9d3e1bcb82", size = 250231, upload-time = "2026-01-25T12:59:27.888Z" }, - { url = "https://files.pythonhosted.org/packages/e2/42/dd9093f919dc3088cb472893651884bd675e3df3d38a43f9053656dca9a2/coverage-7.13.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d6d16b0f71120e365741bca2cb473ca6fe38930bc5431c5e850ba949f708f892", size = 251888, upload-time = "2026-01-25T12:59:29.636Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a6/0af4053e6e819774626e133c3d6f70fae4d44884bfc4b126cb647baee8d3/coverage-7.13.2-cp314-cp314-win32.whl", hash = "sha256:9b2f4714bb7d99ba3790ee095b3b4ac94767e1347fe424278a0b10acb3ff04fe", size = 221968, upload-time = "2026-01-25T12:59:31.424Z" }, - { url = "https://files.pythonhosted.org/packages/c4/cc/5aff1e1f80d55862442855517bb8ad8ad3a68639441ff6287dde6a58558b/coverage-7.13.2-cp314-cp314-win_amd64.whl", hash = "sha256:e4121a90823a063d717a96e0a0529c727fb31ea889369a0ee3ec00ed99bf6859", size = 222783, upload-time = "2026-01-25T12:59:33.118Z" }, - { url = "https://files.pythonhosted.org/packages/de/20/09abafb24f84b3292cc658728803416c15b79f9ee5e68d25238a895b07d9/coverage-7.13.2-cp314-cp314-win_arm64.whl", hash = "sha256:6873f0271b4a15a33e7590f338d823f6f66f91ed147a03938d7ce26efd04eee6", size = 221348, upload-time = "2026-01-25T12:59:34.939Z" }, - { url = "https://files.pythonhosted.org/packages/b6/60/a3820c7232db63be060e4019017cd3426751c2699dab3c62819cdbcea387/coverage-7.13.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:f61d349f5b7cd95c34017f1927ee379bfbe9884300d74e07cf630ccf7a610c1b", size = 219950, upload-time = "2026-01-25T12:59:36.624Z" }, - { url = "https://files.pythonhosted.org/packages/fd/37/e4ef5975fdeb86b1e56db9a82f41b032e3d93a840ebaf4064f39e770d5c5/coverage-7.13.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a43d34ce714f4ca674c0d90beb760eb05aad906f2c47580ccee9da8fe8bfb417", size = 220209, upload-time = "2026-01-25T12:59:38.339Z" }, - { url = "https://files.pythonhosted.org/packages/54/df/d40e091d00c51adca1e251d3b60a8b464112efa3004949e96a74d7c19a64/coverage-7.13.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bff1b04cb9d4900ce5c56c4942f047dc7efe57e2608cb7c3c8936e9970ccdbee", size = 261576, upload-time = "2026-01-25T12:59:40.446Z" }, - { url = "https://files.pythonhosted.org/packages/c5/44/5259c4bed54e3392e5c176121af9f71919d96dde853386e7730e705f3520/coverage-7.13.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6ae99e4560963ad8e163e819e5d77d413d331fd00566c1e0856aa252303552c1", size = 263704, upload-time = "2026-01-25T12:59:42.346Z" }, - { url = "https://files.pythonhosted.org/packages/16/bd/ae9f005827abcbe2c70157459ae86053971c9fa14617b63903abbdce26d9/coverage-7.13.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e79a8c7d461820257d9aa43716c4efc55366d7b292e46b5b37165be1d377405d", size = 266109, upload-time = "2026-01-25T12:59:44.073Z" }, - { url = "https://files.pythonhosted.org/packages/a2/c0/8e279c1c0f5b1eaa3ad9b0fb7a5637fc0379ea7d85a781c0fe0bb3cfc2ab/coverage-7.13.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:060ee84f6a769d40c492711911a76811b4befb6fba50abb450371abb720f5bd6", size = 260686, upload-time = "2026-01-25T12:59:45.804Z" }, - { url = "https://files.pythonhosted.org/packages/b2/47/3a8112627e9d863e7cddd72894171c929e94491a597811725befdcd76bce/coverage-7.13.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bca209d001fd03ea2d978f8a4985093240a355c93078aee3f799852c23f561a", size = 263568, upload-time = "2026-01-25T12:59:47.929Z" }, - { url = "https://files.pythonhosted.org/packages/92/bc/7ea367d84afa3120afc3ce6de294fd2dcd33b51e2e7fbe4bbfd200f2cb8c/coverage-7.13.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:6b8092aa38d72f091db61ef83cb66076f18f02da3e1a75039a4f218629600e04", size = 261174, upload-time = "2026-01-25T12:59:49.717Z" }, - { url = "https://files.pythonhosted.org/packages/33/b7/f1092dcecb6637e31cc2db099581ee5c61a17647849bae6b8261a2b78430/coverage-7.13.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4a3158dc2dcce5200d91ec28cd315c999eebff355437d2765840555d765a6e5f", size = 260017, upload-time = "2026-01-25T12:59:51.463Z" }, - { url = "https://files.pythonhosted.org/packages/2b/cd/f3d07d4b95fbe1a2ef0958c15da614f7e4f557720132de34d2dc3aa7e911/coverage-7.13.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3973f353b2d70bd9796cc12f532a05945232ccae966456c8ed7034cb96bbfd6f", size = 262337, upload-time = "2026-01-25T12:59:53.407Z" }, - { url = "https://files.pythonhosted.org/packages/e0/db/b0d5b2873a07cb1e06a55d998697c0a5a540dcefbf353774c99eb3874513/coverage-7.13.2-cp314-cp314t-win32.whl", hash = "sha256:79f6506a678a59d4ded048dc72f1859ebede8ec2b9a2d509ebe161f01c2879d3", size = 222749, upload-time = "2026-01-25T12:59:56.316Z" }, - { url = "https://files.pythonhosted.org/packages/e5/2f/838a5394c082ac57d85f57f6aba53093b30d9089781df72412126505716f/coverage-7.13.2-cp314-cp314t-win_amd64.whl", hash = "sha256:196bfeabdccc5a020a57d5a368c681e3a6ceb0447d153aeccc1ab4d70a5032ba", size = 223857, upload-time = "2026-01-25T12:59:58.201Z" }, - { url = "https://files.pythonhosted.org/packages/44/d4/b608243e76ead3a4298824b50922b89ef793e50069ce30316a65c1b4d7ef/coverage-7.13.2-cp314-cp314t-win_arm64.whl", hash = "sha256:69269ab58783e090bfbf5b916ab3d188126e22d6070bbfc93098fdd474ef937c", size = 221881, upload-time = "2026-01-25T13:00:00.449Z" }, - { url = "https://files.pythonhosted.org/packages/d2/db/d291e30fdf7ea617a335531e72294e0c723356d7fdde8fba00610a76bda9/coverage-7.13.2-py3-none-any.whl", hash = "sha256:40ce1ea1e25125556d8e76bd0b61500839a07944cc287ac21d5626f3e620cad5", size = 210943, upload-time = "2026-01-25T13:00:02.388Z" }, + { url = "https://files.pythonhosted.org/packages/94/44/330f8e83b143f6668778ed61d17ece9dc48459e9e74669177de02f45fec5/coverage-7.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ed48b4170caa2c4420e0cd27dc977caaffc7eecc317355751df8373dddcef595", size = 219441, upload-time = "2026-02-03T14:00:22.585Z" }, + { url = "https://files.pythonhosted.org/packages/08/e7/29db05693562c2e65bdf6910c0af2fd6f9325b8f43caf7a258413f369e30/coverage-7.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8f2adf4bcffbbec41f366f2e6dffb9d24e8172d16e91da5799c9b7ed6b5716e6", size = 219801, upload-time = "2026-02-03T14:00:24.186Z" }, + { url = "https://files.pythonhosted.org/packages/90/ae/7f8a78249b02b0818db46220795f8ac8312ea4abd1d37d79ea81db5cae81/coverage-7.13.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:01119735c690786b6966a1e9f098da4cd7ca9174c4cfe076d04e653105488395", size = 251306, upload-time = "2026-02-03T14:00:25.798Z" }, + { url = "https://files.pythonhosted.org/packages/62/71/a18a53d1808e09b2e9ebd6b47dad5e92daf4c38b0686b4c4d1b2f3e42b7f/coverage-7.13.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8bb09e83c603f152d855f666d70a71765ca8e67332e5829e62cb9466c176af23", size = 254051, upload-time = "2026-02-03T14:00:27.474Z" }, + { url = "https://files.pythonhosted.org/packages/4a/0a/eb30f6455d04c5a3396d0696cad2df0269ae7444bb322f86ffe3376f7bf9/coverage-7.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b607a40cba795cfac6d130220d25962931ce101f2f478a29822b19755377fb34", size = 255160, upload-time = "2026-02-03T14:00:29.024Z" }, + { url = "https://files.pythonhosted.org/packages/7b/7e/a45baac86274ce3ed842dbb84f14560c673ad30535f397d89164ec56c5df/coverage-7.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:44f14a62f5da2e9aedf9080e01d2cda61df39197d48e323538ec037336d68da8", size = 251709, upload-time = "2026-02-03T14:00:30.641Z" }, + { url = "https://files.pythonhosted.org/packages/c0/df/dd0dc12f30da11349993f3e218901fdf82f45ee44773596050c8f5a1fb25/coverage-7.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:debf29e0b157769843dff0981cc76f79e0ed04e36bb773c6cac5f6029054bd8a", size = 253083, upload-time = "2026-02-03T14:00:32.14Z" }, + { url = "https://files.pythonhosted.org/packages/ab/32/fc764c8389a8ce95cb90eb97af4c32f392ab0ac23ec57cadeefb887188d3/coverage-7.13.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:824bb95cd71604031ae9a48edb91fd6effde669522f960375668ed21b36e3ec4", size = 251227, upload-time = "2026-02-03T14:00:34.721Z" }, + { url = "https://files.pythonhosted.org/packages/dd/ca/d025e9da8f06f24c34d2da9873957cfc5f7e0d67802c3e34d0caa8452130/coverage-7.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8f1010029a5b52dc427c8e2a8dbddb2303ddd180b806687d1acd1bb1d06649e7", size = 250794, upload-time = "2026-02-03T14:00:36.278Z" }, + { url = "https://files.pythonhosted.org/packages/45/c7/76bf35d5d488ec8f68682eb8e7671acc50a6d2d1c1182de1d2b6d4ffad3b/coverage-7.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cd5dee4fd7659d8306ffa79eeaaafd91fa30a302dac3af723b9b469e549247e0", size = 252671, upload-time = "2026-02-03T14:00:38.368Z" }, + { url = "https://files.pythonhosted.org/packages/bf/10/1921f1a03a7c209e1cb374f81a6b9b68b03cdb3ecc3433c189bc90e2a3d5/coverage-7.13.3-cp312-cp312-win32.whl", hash = "sha256:f7f153d0184d45f3873b3ad3ad22694fd73aadcb8cdbc4337ab4b41ea6b4dff1", size = 221986, upload-time = "2026-02-03T14:00:40.442Z" }, + { url = "https://files.pythonhosted.org/packages/3c/7c/f5d93297f8e125a80c15545edc754d93e0ed8ba255b65e609b185296af01/coverage-7.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:03a6e5e1e50819d6d7436f5bc40c92ded7e484e400716886ac921e35c133149d", size = 222793, upload-time = "2026-02-03T14:00:42.106Z" }, + { url = "https://files.pythonhosted.org/packages/43/59/c86b84170015b4555ebabca8649bdf9f4a1f737a73168088385ed0f947c4/coverage-7.13.3-cp312-cp312-win_arm64.whl", hash = "sha256:51c4c42c0e7d09a822b08b6cf79b3c4db8333fffde7450da946719ba0d45730f", size = 221410, upload-time = "2026-02-03T14:00:43.726Z" }, + { url = "https://files.pythonhosted.org/packages/81/f3/4c333da7b373e8c8bfb62517e8174a01dcc373d7a9083698e3b39d50d59c/coverage-7.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:853c3d3c79ff0db65797aad79dee6be020efd218ac4510f15a205f1e8d13ce25", size = 219468, upload-time = "2026-02-03T14:00:45.829Z" }, + { url = "https://files.pythonhosted.org/packages/d6/31/0714337b7d23630c8de2f4d56acf43c65f8728a45ed529b34410683f7217/coverage-7.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f75695e157c83d374f88dcc646a60cb94173304a9258b2e74ba5a66b7614a51a", size = 219839, upload-time = "2026-02-03T14:00:47.407Z" }, + { url = "https://files.pythonhosted.org/packages/12/99/bd6f2a2738144c98945666f90cae446ed870cecf0421c767475fcf42cdbe/coverage-7.13.3-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2d098709621d0819039f3f1e471ee554f55a0b2ac0d816883c765b14129b5627", size = 250828, upload-time = "2026-02-03T14:00:49.029Z" }, + { url = "https://files.pythonhosted.org/packages/6f/99/97b600225fbf631e6f5bfd3ad5bcaf87fbb9e34ff87492e5a572ff01bbe2/coverage-7.13.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:16d23d6579cf80a474ad160ca14d8b319abaa6db62759d6eef53b2fc979b58c8", size = 253432, upload-time = "2026-02-03T14:00:50.655Z" }, + { url = "https://files.pythonhosted.org/packages/5f/5c/abe2b3490bda26bd4f5e3e799be0bdf00bd81edebedc2c9da8d3ef288fa8/coverage-7.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:00d34b29a59d2076e6f318b30a00a69bf63687e30cd882984ed444e753990cc1", size = 254672, upload-time = "2026-02-03T14:00:52.757Z" }, + { url = "https://files.pythonhosted.org/packages/31/ba/5d1957c76b40daff53971fe0adb84d9c2162b614280031d1d0653dd010c1/coverage-7.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ab6d72bffac9deb6e6cb0f61042e748de3f9f8e98afb0375a8e64b0b6e11746b", size = 251050, upload-time = "2026-02-03T14:00:54.332Z" }, + { url = "https://files.pythonhosted.org/packages/69/dc/dffdf3bfe9d32090f047d3c3085378558cb4eb6778cda7de414ad74581ed/coverage-7.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e129328ad1258e49cae0123a3b5fcb93d6c2fa90d540f0b4c7cdcdc019aaa3dc", size = 252801, upload-time = "2026-02-03T14:00:56.121Z" }, + { url = "https://files.pythonhosted.org/packages/87/51/cdf6198b0f2746e04511a30dc9185d7b8cdd895276c07bdb538e37f1cd50/coverage-7.13.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2213a8d88ed35459bda71597599d4eec7c2ebad201c88f0bfc2c26fd9b0dd2ea", size = 250763, upload-time = "2026-02-03T14:00:58.719Z" }, + { url = "https://files.pythonhosted.org/packages/d7/1a/596b7d62218c1d69f2475b69cc6b211e33c83c902f38ee6ae9766dd422da/coverage-7.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:00dd3f02de6d5f5c9c3d95e3e036c3c2e2a669f8bf2d3ceb92505c4ce7838f67", size = 250587, upload-time = "2026-02-03T14:01:01.197Z" }, + { url = "https://files.pythonhosted.org/packages/f7/46/52330d5841ff660f22c130b75f5e1dd3e352c8e7baef5e5fef6b14e3e991/coverage-7.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f9bada7bc660d20b23d7d312ebe29e927b655cf414dadcdb6335a2075695bd86", size = 252358, upload-time = "2026-02-03T14:01:02.824Z" }, + { url = "https://files.pythonhosted.org/packages/36/8a/e69a5be51923097ba7d5cff9724466e74fe486e9232020ba97c809a8b42b/coverage-7.13.3-cp313-cp313-win32.whl", hash = "sha256:75b3c0300f3fa15809bd62d9ca8b170eb21fcf0100eb4b4154d6dc8b3a5bbd43", size = 222007, upload-time = "2026-02-03T14:01:04.876Z" }, + { url = "https://files.pythonhosted.org/packages/0a/09/a5a069bcee0d613bdd48ee7637fa73bc09e7ed4342b26890f2df97cc9682/coverage-7.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:a2f7589c6132c44c53f6e705e1a6677e2b7821378c22f7703b2cf5388d0d4587", size = 222812, upload-time = "2026-02-03T14:01:07.296Z" }, + { url = "https://files.pythonhosted.org/packages/3d/4f/d62ad7dfe32f9e3d4a10c178bb6f98b10b083d6e0530ca202b399371f6c1/coverage-7.13.3-cp313-cp313-win_arm64.whl", hash = "sha256:123ceaf2b9d8c614f01110f908a341e05b1b305d6b2ada98763b9a5a59756051", size = 221433, upload-time = "2026-02-03T14:01:09.156Z" }, + { url = "https://files.pythonhosted.org/packages/04/b2/4876c46d723d80b9c5b695f1a11bf5f7c3dabf540ec00d6edc076ff025e6/coverage-7.13.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:cc7fd0f726795420f3678ac82ff882c7fc33770bd0074463b5aef7293285ace9", size = 220162, upload-time = "2026-02-03T14:01:11.409Z" }, + { url = "https://files.pythonhosted.org/packages/fc/04/9942b64a0e0bdda2c109f56bda42b2a59d9d3df4c94b85a323c1cae9fc77/coverage-7.13.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d358dc408edc28730aed5477a69338e444e62fba0b7e9e4a131c505fadad691e", size = 220510, upload-time = "2026-02-03T14:01:13.038Z" }, + { url = "https://files.pythonhosted.org/packages/5a/82/5cfe1e81eae525b74669f9795f37eb3edd4679b873d79d1e6c1c14ee6c1c/coverage-7.13.3-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5d67b9ed6f7b5527b209b24b3df9f2e5bf0198c1bbf99c6971b0e2dcb7e2a107", size = 261801, upload-time = "2026-02-03T14:01:14.674Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ec/a553d7f742fd2cd12e36a16a7b4b3582d5934b496ef2b5ea8abeb10903d4/coverage-7.13.3-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59224bfb2e9b37c1335ae35d00daa3a5b4e0b1a20f530be208fff1ecfa436f43", size = 263882, upload-time = "2026-02-03T14:01:16.343Z" }, + { url = "https://files.pythonhosted.org/packages/e1/58/8f54a2a93e3d675635bc406de1c9ac8d551312142ff52c9d71b5e533ad45/coverage-7.13.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae9306b5299e31e31e0d3b908c66bcb6e7e3ddca143dea0266e9ce6c667346d3", size = 266306, upload-time = "2026-02-03T14:01:18.02Z" }, + { url = "https://files.pythonhosted.org/packages/1a/be/e593399fd6ea1f00aee79ebd7cc401021f218d34e96682a92e1bae092ff6/coverage-7.13.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:343aaeb5f8bb7bcd38620fd7bc56e6ee8207847d8c6103a1e7b72322d381ba4a", size = 261051, upload-time = "2026-02-03T14:01:19.757Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e5/e9e0f6138b21bcdebccac36fbfde9cf15eb1bbcea9f5b1f35cd1f465fb91/coverage-7.13.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b2182129f4c101272ff5f2f18038d7b698db1bf8e7aa9e615cb48440899ad32e", size = 263868, upload-time = "2026-02-03T14:01:21.487Z" }, + { url = "https://files.pythonhosted.org/packages/9a/bf/de72cfebb69756f2d4a2dde35efcc33c47d85cd3ebdf844b3914aac2ef28/coverage-7.13.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:94d2ac94bd0cc57c5626f52f8c2fffed1444b5ae8c9fc68320306cc2b255e155", size = 261498, upload-time = "2026-02-03T14:01:23.097Z" }, + { url = "https://files.pythonhosted.org/packages/f2/91/4a2d313a70fc2e98ca53afd1c8ce67a89b1944cd996589a5b1fe7fbb3e5c/coverage-7.13.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:65436cde5ecabe26fb2f0bf598962f0a054d3f23ad529361326ac002c61a2a1e", size = 260394, upload-time = "2026-02-03T14:01:24.949Z" }, + { url = "https://files.pythonhosted.org/packages/40/83/25113af7cf6941e779eb7ed8de2a677865b859a07ccee9146d4cc06a03e3/coverage-7.13.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:db83b77f97129813dbd463a67e5335adc6a6a91db652cc085d60c2d512746f96", size = 262579, upload-time = "2026-02-03T14:01:26.703Z" }, + { url = "https://files.pythonhosted.org/packages/1e/19/a5f2b96262977e82fb9aabbe19b4d83561f5d063f18dde3e72f34ffc3b2f/coverage-7.13.3-cp313-cp313t-win32.whl", hash = "sha256:dfb428e41377e6b9ba1b0a32df6db5409cb089a0ed1d0a672dc4953ec110d84f", size = 222679, upload-time = "2026-02-03T14:01:28.553Z" }, + { url = "https://files.pythonhosted.org/packages/81/82/ef1747b88c87a5c7d7edc3704799ebd650189a9158e680a063308b6125ef/coverage-7.13.3-cp313-cp313t-win_amd64.whl", hash = "sha256:5badd7e596e6b0c89aa8ec6d37f4473e4357f982ce57f9a2942b0221cd9cf60c", size = 223740, upload-time = "2026-02-03T14:01:30.776Z" }, + { url = "https://files.pythonhosted.org/packages/1c/4c/a67c7bb5b560241c22736a9cb2f14c5034149ffae18630323fde787339e4/coverage-7.13.3-cp313-cp313t-win_arm64.whl", hash = "sha256:989aa158c0eb19d83c76c26f4ba00dbb272485c56e452010a3450bdbc9daafd9", size = 221996, upload-time = "2026-02-03T14:01:32.495Z" }, + { url = "https://files.pythonhosted.org/packages/5e/b3/677bb43427fed9298905106f39c6520ac75f746f81b8f01104526a8026e4/coverage-7.13.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c6f6169bbdbdb85aab8ac0392d776948907267fcc91deeacf6f9d55f7a83ae3b", size = 219513, upload-time = "2026-02-03T14:01:34.29Z" }, + { url = "https://files.pythonhosted.org/packages/42/53/290046e3bbf8986cdb7366a42dab3440b9983711eaff044a51b11006c67b/coverage-7.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2f5e731627a3d5ef11a2a35aa0c6f7c435867c7ccbc391268eb4f2ca5dbdcc10", size = 219850, upload-time = "2026-02-03T14:01:35.984Z" }, + { url = "https://files.pythonhosted.org/packages/ea/2b/ab41f10345ba2e49d5e299be8663be2b7db33e77ac1b85cd0af985ea6406/coverage-7.13.3-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9db3a3285d91c0b70fab9f39f0a4aa37d375873677efe4e71e58d8321e8c5d39", size = 250886, upload-time = "2026-02-03T14:01:38.287Z" }, + { url = "https://files.pythonhosted.org/packages/72/2d/b3f6913ee5a1d5cdd04106f257e5fac5d048992ffc2d9995d07b0f17739f/coverage-7.13.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:06e49c5897cb12e3f7ecdc111d44e97c4f6d0557b81a7a0204ed70a8b038f86f", size = 253393, upload-time = "2026-02-03T14:01:40.118Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f6/b1f48810ffc6accf49a35b9943636560768f0812330f7456aa87dc39aff5/coverage-7.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb25061a66802df9fc13a9ba1967d25faa4dae0418db469264fd9860a921dde4", size = 254740, upload-time = "2026-02-03T14:01:42.413Z" }, + { url = "https://files.pythonhosted.org/packages/57/d0/e59c54f9be0b61808f6bc4c8c4346bd79f02dd6bbc3f476ef26124661f20/coverage-7.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:99fee45adbb1caeb914da16f70e557fb7ff6ddc9e4b14de665bd41af631367ef", size = 250905, upload-time = "2026-02-03T14:01:44.163Z" }, + { url = "https://files.pythonhosted.org/packages/d5/f7/5291bcdf498bafbee3796bb32ef6966e9915aebd4d0954123c8eae921c32/coverage-7.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:318002f1fd819bdc1651c619268aa5bc853c35fa5cc6d1e8c96bd9cd6c828b75", size = 252753, upload-time = "2026-02-03T14:01:45.974Z" }, + { url = "https://files.pythonhosted.org/packages/a0/a9/1dcafa918c281554dae6e10ece88c1add82db685be123e1b05c2056ff3fb/coverage-7.13.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:71295f2d1d170b9977dc386d46a7a1b7cbb30e5405492529b4c930113a33f895", size = 250716, upload-time = "2026-02-03T14:01:48.844Z" }, + { url = "https://files.pythonhosted.org/packages/44/bb/4ea4eabcce8c4f6235df6e059fbc5db49107b24c4bdffc44aee81aeca5a8/coverage-7.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5b1ad2e0dc672625c44bc4fe34514602a9fd8b10d52ddc414dc585f74453516c", size = 250530, upload-time = "2026-02-03T14:01:50.793Z" }, + { url = "https://files.pythonhosted.org/packages/6d/31/4a6c9e6a71367e6f923b27b528448c37f4e959b7e4029330523014691007/coverage-7.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b2beb64c145593a50d90db5c7178f55daeae129123b0d265bdb3cbec83e5194a", size = 252186, upload-time = "2026-02-03T14:01:52.607Z" }, + { url = "https://files.pythonhosted.org/packages/27/92/e1451ef6390a4f655dc42da35d9971212f7abbbcad0bdb7af4407897eb76/coverage-7.13.3-cp314-cp314-win32.whl", hash = "sha256:3d1aed4f4e837a832df2f3b4f68a690eede0de4560a2dbc214ea0bc55aabcdb4", size = 222253, upload-time = "2026-02-03T14:01:55.071Z" }, + { url = "https://files.pythonhosted.org/packages/8a/98/78885a861a88de020c32a2693487c37d15a9873372953f0c3c159d575a43/coverage-7.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9f9efbbaf79f935d5fbe3ad814825cbce4f6cdb3054384cb49f0c0f496125fa0", size = 223069, upload-time = "2026-02-03T14:01:56.95Z" }, + { url = "https://files.pythonhosted.org/packages/eb/fb/3784753a48da58a5337972abf7ca58b1fb0f1bda21bc7b4fae992fd28e47/coverage-7.13.3-cp314-cp314-win_arm64.whl", hash = "sha256:31b6e889c53d4e6687ca63706148049494aace140cffece1c4dc6acadb70a7b3", size = 221633, upload-time = "2026-02-03T14:01:58.758Z" }, + { url = "https://files.pythonhosted.org/packages/40/f9/75b732d9674d32cdbffe801ed5f770786dd1c97eecedef2125b0d25102dc/coverage-7.13.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c5e9787cec750793a19a28df7edd85ac4e49d3fb91721afcdc3b86f6c08d9aa8", size = 220243, upload-time = "2026-02-03T14:02:01.109Z" }, + { url = "https://files.pythonhosted.org/packages/cf/7e/2868ec95de5a65703e6f0c87407ea822d1feb3619600fbc3c1c4fa986090/coverage-7.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e5b86db331c682fd0e4be7098e6acee5e8a293f824d41487c667a93705d415ca", size = 220515, upload-time = "2026-02-03T14:02:02.862Z" }, + { url = "https://files.pythonhosted.org/packages/7d/eb/9f0d349652fced20bcaea0f67fc5777bd097c92369f267975732f3dc5f45/coverage-7.13.3-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:edc7754932682d52cf6e7a71806e529ecd5ce660e630e8bd1d37109a2e5f63ba", size = 261874, upload-time = "2026-02-03T14:02:04.727Z" }, + { url = "https://files.pythonhosted.org/packages/ee/a5/6619bc4a6c7b139b16818149a3e74ab2e21599ff9a7b6811b6afde99f8ec/coverage-7.13.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3a16d6398666510a6886f67f43d9537bfd0e13aca299688a19daa84f543122f", size = 264004, upload-time = "2026-02-03T14:02:06.634Z" }, + { url = "https://files.pythonhosted.org/packages/29/b7/90aa3fc645a50c6f07881fca4fd0ba21e3bfb6ce3a7078424ea3a35c74c9/coverage-7.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:303d38b19626c1981e1bb067a9928236d88eb0e4479b18a74812f05a82071508", size = 266408, upload-time = "2026-02-03T14:02:09.037Z" }, + { url = "https://files.pythonhosted.org/packages/62/55/08bb2a1e4dcbae384e638f0effef486ba5987b06700e481691891427d879/coverage-7.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:284e06eadfe15ddfee2f4ee56631f164ef897a7d7d5a15bca5f0bb88889fc5ba", size = 260977, upload-time = "2026-02-03T14:02:11.755Z" }, + { url = "https://files.pythonhosted.org/packages/9b/76/8bd4ae055a42d8fb5dd2230e5cf36ff2e05f85f2427e91b11a27fea52ed7/coverage-7.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d401f0864a1d3198422816878e4e84ca89ec1c1bf166ecc0ae01380a39b888cd", size = 263868, upload-time = "2026-02-03T14:02:13.565Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f9/ba000560f11e9e32ec03df5aa8477242c2d95b379c99ac9a7b2e7fbacb1a/coverage-7.13.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3f379b02c18a64de78c4ccdddf1c81c2c5ae1956c72dacb9133d7dd7809794ab", size = 261474, upload-time = "2026-02-03T14:02:16.069Z" }, + { url = "https://files.pythonhosted.org/packages/90/4b/4de4de8f9ca7af4733bfcf4baa440121b7dbb3856daf8428ce91481ff63b/coverage-7.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:7a482f2da9086971efb12daca1d6547007ede3674ea06e16d7663414445c683e", size = 260317, upload-time = "2026-02-03T14:02:17.996Z" }, + { url = "https://files.pythonhosted.org/packages/05/71/5cd8436e2c21410ff70be81f738c0dddea91bcc3189b1517d26e0102ccb3/coverage-7.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:562136b0d401992118d9b49fbee5454e16f95f85b120a4226a04d816e33fe024", size = 262635, upload-time = "2026-02-03T14:02:20.405Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f8/2834bb45bdd70b55a33ec354b8b5f6062fc90e5bb787e14385903a979503/coverage-7.13.3-cp314-cp314t-win32.whl", hash = "sha256:ca46e5c3be3b195098dd88711890b8011a9fa4feca942292bb84714ce5eab5d3", size = 223035, upload-time = "2026-02-03T14:02:22.323Z" }, + { url = "https://files.pythonhosted.org/packages/26/75/f8290f0073c00d9ae14056d2b84ab92dff21d5370e464cb6cb06f52bf580/coverage-7.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:06d316dbb3d9fd44cca05b2dbcfbef22948493d63a1f28e828d43e6cc505fed8", size = 224142, upload-time = "2026-02-03T14:02:24.143Z" }, + { url = "https://files.pythonhosted.org/packages/03/01/43ac78dfea8946c4a9161bbc034b5549115cb2b56781a4b574927f0d141a/coverage-7.13.3-cp314-cp314t-win_arm64.whl", hash = "sha256:299d66e9218193f9dc6e4880629ed7c4cd23486005166247c283fb98531656c3", size = 222166, upload-time = "2026-02-03T14:02:26.005Z" }, + { url = "https://files.pythonhosted.org/packages/7d/fb/70af542d2d938c778c9373ce253aa4116dbe7c0a5672f78b2b2ae0e1b94b/coverage-7.13.3-py3-none-any.whl", hash = "sha256:90a8af9dba6429b2573199622d72e0ebf024d6276f16abce394ad4d181bb0910", size = 211237, upload-time = "2026-02-03T14:02:27.986Z" }, ] [[package]] @@ -420,7 +420,7 @@ dev = [ { name = "poethepoet", specifier = ">=0.40.0" }, { name = "psutil", specifier = ">=7.2.2" }, { name = "pytest-cov", specifier = ">=7.0.0" }, - { name = "ruff", specifier = ">=0.14.14" }, + { name = "ruff", specifier = ">=0.15.0" }, ] test = [ { name = "psutil", specifier = ">=7.2.2" }, @@ -429,31 +429,31 @@ test = [ [[package]] name = "pytokens" -version = "0.4.0" +version = "0.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/16/4b9cfd90d55e66ffdb277d7ebe3bc25250c2311336ec3fc73b2673c794d5/pytokens-0.4.0.tar.gz", hash = "sha256:6b0b03e6ea7c9f9d47c5c61164b69ad30f4f0d70a5d9fe7eac4d19f24f77af2d", size = 15039, upload-time = "2026-01-19T07:59:50.623Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/65/65460ebbfefd0bc1b160457904370d44f269e6e4582e0a9b6cba7c267b04/pytokens-0.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cd8da894e5a29ba6b6da8be06a4f7589d7220c099b5e363cb0643234b9b38c2a", size = 159864, upload-time = "2026-01-19T07:59:08.908Z" }, - { url = "https://files.pythonhosted.org/packages/25/70/a46669ec55876c392036b4da9808b5c3b1c5870bbca3d4cc923bf68bdbc1/pytokens-0.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:237ba7cfb677dbd3b01b09860810aceb448871150566b93cd24501d5734a04b1", size = 254448, upload-time = "2026-01-19T07:59:10.594Z" }, - { url = "https://files.pythonhosted.org/packages/62/0b/c486fc61299c2fc3b7f88ee4e115d4c8b6ffd1a7f88dc94b398b5b1bc4b8/pytokens-0.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01d1a61e36812e4e971cfe2c0e4c1f2d66d8311031dac8bf168af8a249fa04dd", size = 268863, upload-time = "2026-01-19T07:59:12.31Z" }, - { url = "https://files.pythonhosted.org/packages/79/92/b036af846707d25feaff7cafbd5280f1bd6a1034c16bb06a7c910209c1ab/pytokens-0.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e47e2ef3ec6ee86909e520d79f965f9b23389fda47460303cf715d510a6fe544", size = 267181, upload-time = "2026-01-19T07:59:13.856Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c0/6d011fc00fefa74ce34816c84a923d2dd7c46b8dbc6ee52d13419786834c/pytokens-0.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:3d36954aba4557fd5a418a03cf595ecbb1cdcce119f91a49b19ef09d691a22ae", size = 102814, upload-time = "2026-01-19T07:59:15.288Z" }, - { url = "https://files.pythonhosted.org/packages/98/63/627b7e71d557383da5a97f473ad50f8d9c2c1f55c7d3c2531a120c796f6e/pytokens-0.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73eff3bdd8ad08da679867992782568db0529b887bed4c85694f84cdf35eafc6", size = 159744, upload-time = "2026-01-19T07:59:16.88Z" }, - { url = "https://files.pythonhosted.org/packages/28/d7/16f434c37ec3824eba6bcb6e798e5381a8dc83af7a1eda0f95c16fe3ade5/pytokens-0.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d97cc1f91b1a8e8ebccf31c367f28225699bea26592df27141deade771ed0afb", size = 253207, upload-time = "2026-01-19T07:59:18.069Z" }, - { url = "https://files.pythonhosted.org/packages/ab/96/04102856b9527701ae57d74a6393d1aca5bad18a1b1ca48ccffb3c93b392/pytokens-0.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c8952c537cb73a1a74369501a83b7f9d208c3cf92c41dd88a17814e68d48ce", size = 267452, upload-time = "2026-01-19T07:59:19.328Z" }, - { url = "https://files.pythonhosted.org/packages/0e/ef/0936eb472b89ab2d2c2c24bb81c50417e803fa89c731930d9fb01176fe9f/pytokens-0.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5dbf56f3c748aed9310b310d5b8b14e2c96d3ad682ad5a943f381bdbbdddf753", size = 265965, upload-time = "2026-01-19T07:59:20.613Z" }, - { url = "https://files.pythonhosted.org/packages/ae/f5/64f3d6f7df4a9e92ebda35ee85061f6260e16eac82df9396020eebbca775/pytokens-0.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:e131804513597f2dff2b18f9911d9b6276e21ef3699abeffc1c087c65a3d975e", size = 102813, upload-time = "2026-01-19T07:59:22.012Z" }, - { url = "https://files.pythonhosted.org/packages/5f/f1/d07e6209f18ef378fc2ae9dee8d1dfe91fd2447c2e2dbfa32867b6dd30cf/pytokens-0.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0d7374c917197106d3c4761374718bc55ea2e9ac0fb94171588ef5840ee1f016", size = 159968, upload-time = "2026-01-19T07:59:23.07Z" }, - { url = "https://files.pythonhosted.org/packages/0a/73/0eb111400abd382a04f253b269819db9fcc748aa40748441cebdcb6d068f/pytokens-0.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cd3fa1caf9e47a72ee134a29ca6b5bea84712724bba165d6628baa190c6ea5b", size = 253373, upload-time = "2026-01-19T07:59:24.381Z" }, - { url = "https://files.pythonhosted.org/packages/bd/8d/9e4e2fdb5bcaba679e54afcc304e9f13f488eb4d626e6b613f9553e03dbd/pytokens-0.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c6986576b7b07fe9791854caa5347923005a80b079d45b63b0be70d50cce5f1", size = 267024, upload-time = "2026-01-19T07:59:25.74Z" }, - { url = "https://files.pythonhosted.org/packages/cb/b7/e0a370321af2deb772cff14ff337e1140d1eac2c29a8876bfee995f486f0/pytokens-0.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9940f7c2e2f54fb1cb5fe17d0803c54da7a2bf62222704eb4217433664a186a7", size = 270912, upload-time = "2026-01-19T07:59:27.072Z" }, - { url = "https://files.pythonhosted.org/packages/7c/54/4348f916c440d4c3e68b53b4ed0e66b292d119e799fa07afa159566dcc86/pytokens-0.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:54691cf8f299e7efabcc25adb4ce715d3cef1491e1c930eaf555182f898ef66a", size = 103836, upload-time = "2026-01-19T07:59:28.112Z" }, - { url = "https://files.pythonhosted.org/packages/e8/f8/a693c0cfa9c783a2a8c4500b7b2a8bab420f8ca4f2d496153226bf1c12e3/pytokens-0.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:94ff5db97a0d3cd7248a5b07ba2167bd3edc1db92f76c6db00137bbaf068ddf8", size = 167643, upload-time = "2026-01-19T07:59:29.292Z" }, - { url = "https://files.pythonhosted.org/packages/c0/dd/a64eb1e9f3ec277b69b33ef1b40ffbcc8f0a3bafcde120997efc7bdefebf/pytokens-0.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d0dd6261cd9cc95fae1227b1b6ebee023a5fd4a4b6330b071c73a516f5f59b63", size = 289553, upload-time = "2026-01-19T07:59:30.537Z" }, - { url = "https://files.pythonhosted.org/packages/df/22/06c1079d93dbc3bca5d013e1795f3d8b9ed6c87290acd6913c1c526a6bb2/pytokens-0.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cdca8159df407dbd669145af4171a0d967006e0be25f3b520896bc7068f02c4", size = 302490, upload-time = "2026-01-19T07:59:32.352Z" }, - { url = "https://files.pythonhosted.org/packages/8d/de/a6f5e43115b4fbf4b93aa87d6c83c79932cdb084f9711daae04549e1e4ad/pytokens-0.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4b5770abeb2a24347380a1164a558f0ebe06e98aedbd54c45f7929527a5fb26e", size = 305652, upload-time = "2026-01-19T07:59:33.685Z" }, - { url = "https://files.pythonhosted.org/packages/ab/3d/c136e057cb622e36e0c3ff7a8aaa19ff9720050c4078235691da885fe6ee/pytokens-0.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:74500d72c561dad14c037a9e86a657afd63e277dd5a3bb7570932ab7a3b12551", size = 115472, upload-time = "2026-01-19T07:59:34.734Z" }, - { url = "https://files.pythonhosted.org/packages/7c/3c/6941a82f4f130af6e1c68c076b6789069ef10c04559bd4733650f902fd3b/pytokens-0.4.0-py3-none-any.whl", hash = "sha256:0508d11b4de157ee12063901603be87fb0253e8f4cb9305eb168b1202ab92068", size = 13224, upload-time = "2026-01-19T07:59:49.822Z" }, + { url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" }, + { url = "https://files.pythonhosted.org/packages/20/01/7436e9ad693cebda0551203e0bf28f7669976c60ad07d6402098208476de/pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9", size = 268076, upload-time = "2026-01-30T01:03:10.957Z" }, + { url = "https://files.pythonhosted.org/packages/2e/df/533c82a3c752ba13ae7ef238b7f8cdd272cf1475f03c63ac6cf3fcfb00b6/pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68", size = 103552, upload-time = "2026-01-30T01:03:12.066Z" }, + { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" }, + { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" }, + { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" }, + { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" }, + { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" }, + { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" }, + { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" }, + { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" }, + { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" }, + { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" }, + { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" }, + { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" }, + { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" }, ] [[package]] @@ -504,28 +504,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.14.14" +version = "0.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/06/f71e3a86b2df0dfa2d2f72195941cd09b44f87711cb7fa5193732cb9a5fc/ruff-0.14.14.tar.gz", hash = "sha256:2d0f819c9a90205f3a867dbbd0be083bee9912e170fd7d9704cc8ae45824896b", size = 4515732, upload-time = "2026-01-22T22:30:17.527Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/39/5cee96809fbca590abea6b46c6d1c586b49663d1d2830a751cc8fc42c666/ruff-0.15.0.tar.gz", hash = "sha256:6bdea47cdbea30d40f8f8d7d69c0854ba7c15420ec75a26f463290949d7f7e9a", size = 4524893, upload-time = "2026-02-03T17:53:35.357Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/89/20a12e97bc6b9f9f68343952da08a8099c57237aef953a56b82711d55edd/ruff-0.14.14-py3-none-linux_armv6l.whl", hash = "sha256:7cfe36b56e8489dee8fbc777c61959f60ec0f1f11817e8f2415f429552846aed", size = 10467650, upload-time = "2026-01-22T22:30:08.578Z" }, - { url = "https://files.pythonhosted.org/packages/a3/b1/c5de3fd2d5a831fcae21beda5e3589c0ba67eec8202e992388e4b17a6040/ruff-0.14.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6006a0082336e7920b9573ef8a7f52eec837add1265cc74e04ea8a4368cd704c", size = 10883245, upload-time = "2026-01-22T22:30:04.155Z" }, - { url = "https://files.pythonhosted.org/packages/b8/7c/3c1db59a10e7490f8f6f8559d1db8636cbb13dccebf18686f4e3c9d7c772/ruff-0.14.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:026c1d25996818f0bf498636686199d9bd0d9d6341c9c2c3b62e2a0198b758de", size = 10231273, upload-time = "2026-01-22T22:30:34.642Z" }, - { url = "https://files.pythonhosted.org/packages/a1/6e/5e0e0d9674be0f8581d1f5e0f0a04761203affce3232c1a1189d0e3b4dad/ruff-0.14.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f666445819d31210b71e0a6d1c01e24447a20b85458eea25a25fe8142210ae0e", size = 10585753, upload-time = "2026-01-22T22:30:31.781Z" }, - { url = "https://files.pythonhosted.org/packages/23/09/754ab09f46ff1884d422dc26d59ba18b4e5d355be147721bb2518aa2a014/ruff-0.14.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c0f18b922c6d2ff9a5e6c3ee16259adc513ca775bcf82c67ebab7cbd9da5bc8", size = 10286052, upload-time = "2026-01-22T22:30:24.827Z" }, - { url = "https://files.pythonhosted.org/packages/c8/cc/e71f88dd2a12afb5f50733851729d6b571a7c3a35bfdb16c3035132675a0/ruff-0.14.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1629e67489c2dea43e8658c3dba659edbfd87361624b4040d1df04c9740ae906", size = 11043637, upload-time = "2026-01-22T22:30:13.239Z" }, - { url = "https://files.pythonhosted.org/packages/67/b2/397245026352494497dac935d7f00f1468c03a23a0c5db6ad8fc49ca3fb2/ruff-0.14.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:27493a2131ea0f899057d49d303e4292b2cae2bb57253c1ed1f256fbcd1da480", size = 12194761, upload-time = "2026-01-22T22:30:22.542Z" }, - { url = "https://files.pythonhosted.org/packages/5b/06/06ef271459f778323112c51b7587ce85230785cd64e91772034ddb88f200/ruff-0.14.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01ff589aab3f5b539e35db38425da31a57521efd1e4ad1ae08fc34dbe30bd7df", size = 12005701, upload-time = "2026-01-22T22:30:20.499Z" }, - { url = "https://files.pythonhosted.org/packages/41/d6/99364514541cf811ccc5ac44362f88df66373e9fec1b9d1c4cc830593fe7/ruff-0.14.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc12d74eef0f29f51775f5b755913eb523546b88e2d733e1d701fe65144e89b", size = 11282455, upload-time = "2026-01-22T22:29:59.679Z" }, - { url = "https://files.pythonhosted.org/packages/ca/71/37daa46f89475f8582b7762ecd2722492df26421714a33e72ccc9a84d7a5/ruff-0.14.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb8481604b7a9e75eff53772496201690ce2687067e038b3cc31aaf16aa0b974", size = 11215882, upload-time = "2026-01-22T22:29:57.032Z" }, - { url = "https://files.pythonhosted.org/packages/2c/10/a31f86169ec91c0705e618443ee74ede0bdd94da0a57b28e72db68b2dbac/ruff-0.14.14-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:14649acb1cf7b5d2d283ebd2f58d56b75836ed8c6f329664fa91cdea19e76e66", size = 11180549, upload-time = "2026-01-22T22:30:27.175Z" }, - { url = "https://files.pythonhosted.org/packages/fd/1e/c723f20536b5163adf79bdd10c5f093414293cdf567eed9bdb7b83940f3f/ruff-0.14.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e8058d2145566510790eab4e2fad186002e288dec5e0d343a92fe7b0bc1b3e13", size = 10543416, upload-time = "2026-01-22T22:30:01.964Z" }, - { url = "https://files.pythonhosted.org/packages/3e/34/8a84cea7e42c2d94ba5bde1d7a4fae164d6318f13f933d92da6d7c2041ff/ruff-0.14.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e651e977a79e4c758eb807f0481d673a67ffe53cfa92209781dfa3a996cf8412", size = 10285491, upload-time = "2026-01-22T22:30:29.51Z" }, - { url = "https://files.pythonhosted.org/packages/55/ef/b7c5ea0be82518906c978e365e56a77f8de7678c8bb6651ccfbdc178c29f/ruff-0.14.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cc8b22da8d9d6fdd844a68ae937e2a0adf9b16514e9a97cc60355e2d4b219fc3", size = 10733525, upload-time = "2026-01-22T22:30:06.499Z" }, - { url = "https://files.pythonhosted.org/packages/6a/5b/aaf1dfbcc53a2811f6cc0a1759de24e4b03e02ba8762daabd9b6bd8c59e3/ruff-0.14.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:16bc890fb4cc9781bb05beb5ab4cd51be9e7cb376bf1dd3580512b24eb3fda2b", size = 11315626, upload-time = "2026-01-22T22:30:36.848Z" }, - { url = "https://files.pythonhosted.org/packages/2c/aa/9f89c719c467dfaf8ad799b9bae0df494513fb21d31a6059cb5870e57e74/ruff-0.14.14-py3-none-win32.whl", hash = "sha256:b530c191970b143375b6a68e6f743800b2b786bbcf03a7965b06c4bf04568167", size = 10502442, upload-time = "2026-01-22T22:30:38.93Z" }, - { url = "https://files.pythonhosted.org/packages/87/44/90fa543014c45560cae1fffc63ea059fb3575ee6e1cb654562197e5d16fb/ruff-0.14.14-py3-none-win_amd64.whl", hash = "sha256:3dde1435e6b6fe5b66506c1dff67a421d0b7f6488d466f651c07f4cab3bf20fd", size = 11630486, upload-time = "2026-01-22T22:30:10.852Z" }, - { url = "https://files.pythonhosted.org/packages/9e/6a/40fee331a52339926a92e17ae748827270b288a35ef4a15c9c8f2ec54715/ruff-0.14.14-py3-none-win_arm64.whl", hash = "sha256:56e6981a98b13a32236a72a8da421d7839221fa308b223b9283312312e5ac76c", size = 10920448, upload-time = "2026-01-22T22:30:15.417Z" }, + { url = "https://files.pythonhosted.org/packages/bc/88/3fd1b0aa4b6330d6aaa63a285bc96c9f71970351579152d231ed90914586/ruff-0.15.0-py3-none-linux_armv6l.whl", hash = "sha256:aac4ebaa612a82b23d45964586f24ae9bc23ca101919f5590bdb368d74ad5455", size = 10354332, upload-time = "2026-02-03T17:52:54.892Z" }, + { url = "https://files.pythonhosted.org/packages/72/f6/62e173fbb7eb75cc29fe2576a1e20f0a46f671a2587b5f604bfb0eaf5f6f/ruff-0.15.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dcd4be7cc75cfbbca24a98d04d0b9b36a270d0833241f776b788d59f4142b14d", size = 10767189, upload-time = "2026-02-03T17:53:19.778Z" }, + { url = "https://files.pythonhosted.org/packages/99/e4/968ae17b676d1d2ff101d56dc69cf333e3a4c985e1ec23803df84fc7bf9e/ruff-0.15.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d747e3319b2bce179c7c1eaad3d884dc0a199b5f4d5187620530adf9105268ce", size = 10075384, upload-time = "2026-02-03T17:53:29.241Z" }, + { url = "https://files.pythonhosted.org/packages/a2/bf/9843c6044ab9e20af879c751487e61333ca79a2c8c3058b15722386b8cae/ruff-0.15.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:650bd9c56ae03102c51a5e4b554d74d825ff3abe4db22b90fd32d816c2e90621", size = 10481363, upload-time = "2026-02-03T17:52:43.332Z" }, + { url = "https://files.pythonhosted.org/packages/55/d9/4ada5ccf4cd1f532db1c8d44b6f664f2208d3d93acbeec18f82315e15193/ruff-0.15.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6664b7eac559e3048223a2da77769c2f92b43a6dfd4720cef42654299a599c9", size = 10187736, upload-time = "2026-02-03T17:53:00.522Z" }, + { url = "https://files.pythonhosted.org/packages/86/e2/f25eaecd446af7bb132af0a1d5b135a62971a41f5366ff41d06d25e77a91/ruff-0.15.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f811f97b0f092b35320d1556f3353bf238763420ade5d9e62ebd2b73f2ff179", size = 10968415, upload-time = "2026-02-03T17:53:15.705Z" }, + { url = "https://files.pythonhosted.org/packages/e7/dc/f06a8558d06333bf79b497d29a50c3a673d9251214e0d7ec78f90b30aa79/ruff-0.15.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:761ec0a66680fab6454236635a39abaf14198818c8cdf691e036f4bc0f406b2d", size = 11809643, upload-time = "2026-02-03T17:53:23.031Z" }, + { url = "https://files.pythonhosted.org/packages/dd/45/0ece8db2c474ad7df13af3a6d50f76e22a09d078af63078f005057ca59eb/ruff-0.15.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:940f11c2604d317e797b289f4f9f3fa5555ffe4fb574b55ed006c3d9b6f0eb78", size = 11234787, upload-time = "2026-02-03T17:52:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/8a/d9/0e3a81467a120fd265658d127db648e4d3acfe3e4f6f5d4ea79fac47e587/ruff-0.15.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcbca3d40558789126da91d7ef9a7c87772ee107033db7191edefa34e2c7f1b4", size = 11112797, upload-time = "2026-02-03T17:52:49.274Z" }, + { url = "https://files.pythonhosted.org/packages/b2/cb/8c0b3b0c692683f8ff31351dfb6241047fa873a4481a76df4335a8bff716/ruff-0.15.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9a121a96db1d75fa3eb39c4539e607f628920dd72ff1f7c5ee4f1b768ac62d6e", size = 11033133, upload-time = "2026-02-03T17:53:33.105Z" }, + { url = "https://files.pythonhosted.org/packages/f8/5e/23b87370cf0f9081a8c89a753e69a4e8778805b8802ccfe175cc410e50b9/ruff-0.15.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5298d518e493061f2eabd4abd067c7e4fb89e2f63291c94332e35631c07c3662", size = 10442646, upload-time = "2026-02-03T17:53:06.278Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9a/3c94de5ce642830167e6d00b5c75aacd73e6347b4c7fc6828699b150a5ee/ruff-0.15.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:afb6e603d6375ff0d6b0cee563fa21ab570fd15e65c852cb24922cef25050cf1", size = 10195750, upload-time = "2026-02-03T17:53:26.084Z" }, + { url = "https://files.pythonhosted.org/packages/30/15/e396325080d600b436acc970848d69df9c13977942fb62bb8722d729bee8/ruff-0.15.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:77e515f6b15f828b94dc17d2b4ace334c9ddb7d9468c54b2f9ed2b9c1593ef16", size = 10676120, upload-time = "2026-02-03T17:53:09.363Z" }, + { url = "https://files.pythonhosted.org/packages/8d/c9/229a23d52a2983de1ad0fb0ee37d36e0257e6f28bfd6b498ee2c76361874/ruff-0.15.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6f6e80850a01eb13b3e42ee0ebdf6e4497151b48c35051aab51c101266d187a3", size = 11201636, upload-time = "2026-02-03T17:52:57.281Z" }, + { url = "https://files.pythonhosted.org/packages/6f/b0/69adf22f4e24f3677208adb715c578266842e6e6a3cc77483f48dd999ede/ruff-0.15.0-py3-none-win32.whl", hash = "sha256:238a717ef803e501b6d51e0bdd0d2c6e8513fe9eec14002445134d3907cd46c3", size = 10465945, upload-time = "2026-02-03T17:53:12.591Z" }, + { url = "https://files.pythonhosted.org/packages/51/ad/f813b6e2c97e9b4598be25e94a9147b9af7e60523b0cb5d94d307c15229d/ruff-0.15.0-py3-none-win_amd64.whl", hash = "sha256:dd5e4d3301dc01de614da3cdffc33d4b1b96fb89e45721f1598e5532ccf78b18", size = 11564657, upload-time = "2026-02-03T17:52:51.893Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b0/2d823f6e77ebe560f4e397d078487e8d52c1516b331e3521bc75db4272ca/ruff-0.15.0-py3-none-win_arm64.whl", hash = "sha256:c480d632cc0ca3f0727acac8b7d053542d9e114a462a145d0b00e7cd658c515a", size = 10865753, upload-time = "2026-02-03T17:53:03.014Z" }, ] [[package]] From 685035e72656d407c66880f5b9497e162a8e6201 Mon Sep 17 00:00:00 2001 From: ddc Date: Wed, 4 Feb 2026 13:33:44 -0300 Subject: [PATCH 4/9] V6.0.2 --- pyproject.toml | 9 ++++----- uv.lock | 8 ++++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8e59784..65c6f3a 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,6 +10,8 @@ license = {text = "MIT"} readme = "README.md" authors = [{name = "Daniel Costa", email = "danieldcsta@gmail.com"}] maintainers = [{name = "Daniel Costa"}] +urls.Repository = "https://github.com/ddc/pythonLogs" +urls.Homepage = "https://pypi.org/project/pythonLogs" keywords = [ "python3", "python-3", "python", "log", "logging", "logger", @@ -33,14 +35,11 @@ dependencies = [ "pydantic-settings>=2.11.0", ] -[project.urls] -Homepage = "https://pypi.org/project/pythonLogs" -Repository = "https://github.com/ddc/pythonLogs" - [dependency-groups] test = [ "psutil>=7.2.2", - "pytest-cov>=7.0.0", + "pytest>=9.0.2", + "coverage>=7.13.3", "pytest-cov>=7.0.0", ] dev = [ diff --git a/uv.lock b/uv.lock index f0d2350..d810b85 100644 --- a/uv.lock +++ b/uv.lock @@ -401,13 +401,17 @@ dependencies = [ [package.dev-dependencies] dev = [ { name = "black" }, + { name = "coverage" }, { name = "poethepoet" }, { name = "psutil" }, + { name = "pytest" }, { name = "pytest-cov" }, { name = "ruff" }, ] test = [ + { name = "coverage" }, { name = "psutil" }, + { name = "pytest" }, { name = "pytest-cov" }, ] @@ -417,13 +421,17 @@ requires-dist = [{ name = "pydantic-settings", specifier = ">=2.11.0" }] [package.metadata.requires-dev] dev = [ { name = "black", specifier = ">=26.1.0" }, + { name = "coverage", specifier = ">=7.13.3" }, { name = "poethepoet", specifier = ">=0.40.0" }, { name = "psutil", specifier = ">=7.2.2" }, + { name = "pytest", specifier = ">=9.0.2" }, { name = "pytest-cov", specifier = ">=7.0.0" }, { name = "ruff", specifier = ">=0.15.0" }, ] test = [ + { name = "coverage", specifier = ">=7.13.3" }, { name = "psutil", specifier = ">=7.2.2" }, + { name = "pytest", specifier = ">=9.0.2" }, { name = "pytest-cov", specifier = ">=7.0.0" }, ] From 54ca7afb0469f843bb802f94e9c5e0ae8a201594 Mon Sep 17 00:00:00 2001 From: ddc Date: Thu, 5 Feb 2026 15:39:35 -0300 Subject: [PATCH 5/9] V6.0.2 --- .github/PULL_REQUEST_TEMPLATE | 18 +- .github/workflows/workflow.yml | 34 ++-- pyproject.toml | 70 +++---- pythonLogs/.env.example | 1 + pythonLogs/core/constants.py | 6 +- pythonLogs/core/factory.py | 174 +++++++++--------- pythonLogs/core/log_utils.py | 19 +- pythonLogs/core/memory_utils.py | 15 +- pythonLogs/core/settings.py | 4 + pythonLogs/core/thread_safety.py | 17 +- tests/core/test_basic_log.py | 2 +- tests/core/test_log_utils.py | 34 ++-- tests/core/test_log_utils_windows.py | 24 +-- tests/core/test_settings.py | 9 + tests/factory/test_factory.py | 2 +- tests/performance/test_memory_optimization.py | 4 +- tests/performance/test_performance.py | 2 +- .../thread_safety/test_automatic_features.py | 6 +- tests/thread_safety/test_thread_safety.py | 2 +- uv.lock | 133 +++++++++++-- 20 files changed, 355 insertions(+), 221 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE index 0251d90..e1ae10c 100755 --- a/.github/PULL_REQUEST_TEMPLATE +++ b/.github/PULL_REQUEST_TEMPLATE @@ -3,14 +3,14 @@ ## Checklist -- [ ] If code changes were made, then they have been tested. - - [ ] I have updated the documentation to reflect the changes. -- [ ] I have thought about how this code may affect other services. -- [ ] This PR fixes an issue. -- [ ] This PR add/remove/change unit tests. -- [ ] This PR adds something new (e.g. new method or parameters). -- [ ] This PR is a breaking change (e.g. methods or parameters removed/renamed) -- [ ] This PR is **not** a code change (e.g. documentation, README, ...) +- [ ] If code changes were made, then they have been tested +- [ ] I have updated the documentation to reflect any changes made +- [ ] I have thought about how this code may affect other services +- [ ] This PR fixes an issue +- [ ] This PR is a breaking change (e.g. method, parameters, env variables) +- [ ] This PR adds something new (e.g. method, parameters, env variables) +- [ ] This PR change unit and integration tests +- [ ] This PR is **NOT** a code change (e.g. documentation, packages) ## Reviewer -- [ ] I understand that approving this code, I am also responsible for it going into the codebase. +- [ ] I understand that approving this code, I am also responsible for it going into the codebase diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index a51c4f5..657afe1 100755 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -5,8 +5,22 @@ name: CI/CD Pipeline branches: ['**'] tags: ['v*'] +env: + LATEST_PYTHON_VERSION: '3.14' jobs: + lint: + name: Lint (ruff) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: astral-sh/ruff-action@v3 + with: + args: "check" + - uses: astral-sh/ruff-action@v3 + with: + args: "format --check" + test: name: Test Python ${{ matrix.python-version }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} @@ -14,7 +28,7 @@ jobs: fail-fast: false matrix: os: ['ubuntu-latest', 'macos-latest', 'windows-latest'] - python-version: ['3.12', '3.13', '3.14'] + python-version: ['3.11', '3.14'] defaults: run: working-directory: ${{ github.workspace }} @@ -28,7 +42,7 @@ jobs: run: uv python install ${{ matrix.python-version }} - name: Install dependencies - run: uv sync --group test + run: uv sync --group dev shell: bash - name: Run tests with coverage @@ -40,20 +54,19 @@ jobs: shell: bash - name: Upload coverage to Codecov - if: matrix.python-version == '3.14' && matrix.os == 'ubuntu-latest' + if: matrix.python-version == env.LATEST_PYTHON_VERSION && matrix.os == 'ubuntu-latest' uses: codecov/codecov-action@v5 - name: Upload test results to Codecov - if: matrix.python-version == '3.14' && matrix.os == 'ubuntu-latest' + if: matrix.python-version == env.LATEST_PYTHON_VERSION && matrix.os == 'ubuntu-latest' uses: codecov/codecov-action@v5 with: report_type: test_results - build: - name: Build package + name: Build Package runs-on: ubuntu-latest - needs: [test] + needs: [lint, test] if: startsWith(github.ref, 'refs/tags/v') steps: - uses: actions/checkout@v6 @@ -61,8 +74,8 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v7 - - name: Set up Python 3.14 - run: uv python install 3.14 + - name: Set up Python ${{ env.LATEST_PYTHON_VERSION }} + run: uv python install ${{ env.LATEST_PYTHON_VERSION }} - name: Build package run: uv build @@ -74,11 +87,10 @@ jobs: path: dist/ retention-days: 7 - release: name: Create Release runs-on: ubuntu-latest - needs: [build] + needs: build if: startsWith(github.ref, 'refs/tags/v') permissions: contents: write diff --git a/pyproject.toml b/pyproject.toml index 65c6f3a..5b8112a 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,18 +2,31 @@ requires = ["hatchling"] build-backend = "hatchling.build" +[tool.hatch.metadata] +allow-direct-references = true + +[tool.hatch.build] +include = ["pythonLogs/**/*"] + +[tool.hatch.build.targets.wheel] +packages = ["pythonLogs"] + [project] name = "pythonLogs" version = "6.0.2" description = "High-performance Python logging library with file rotation and optimized caching for better performance" -license = {text = "MIT"} -readme = "README.md" -authors = [{name = "Daniel Costa", email = "danieldcsta@gmail.com"}] -maintainers = [{name = "Daniel Costa"}] urls.Repository = "https://github.com/ddc/pythonLogs" urls.Homepage = "https://pypi.org/project/pythonLogs" +license = {text = "MIT"} +readme = "README.md" +authors = [ + {name = "Daniel Costa", email = "danieldcsta@gmail.com"}, +] +maintainers = [ + {name = "Daniel Costa"}, +] keywords = [ - "python3", "python-3", "python", + "python", "python3", "python-3", "log", "logging", "logger", "logutils", "log-utils", "pythonLogs" ] @@ -22,6 +35,7 @@ classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", @@ -30,54 +44,46 @@ classifiers = [ "Intended Audience :: Developers", "Natural Language :: English", ] -requires-python = ">=3.12" +requires-python = ">=3.11" dependencies = [ "pydantic-settings>=2.11.0", ] [dependency-groups] -test = [ +dev = [ "psutil>=7.2.2", "pytest>=9.0.2", - "coverage>=7.13.3", "pytest-cov>=7.0.0", -] -dev = [ - {include-group = "test"}, + "coverage>=7.13.3", "black>=26.1.0", "poethepoet>=0.40.0", "ruff>=0.15.0", ] -[tool.hatch.build] -include = ["pythonLogs/**/*"] - -[tool.hatch.build.targets.wheel] -packages = ["pythonLogs"] - [tool.poe.tasks] -build = "uv build --wheel" -updatedev.shell = "uv lock && uv sync --no-install-project --all-groups" linter.shell = "uv run ruff check --fix . && uv run black ." -profile = "uv run python -m cProfile -o cprofile.prof -m pytest" +profile = "uv run python -m cProfile -o cprofile_unit.prof -m pytest --no-cov" test = "uv run pytest" +updatedev.sequence = ["linter", {shell = "uv lock && uv sync --all-extras --group dev"}] +build.sequence = ["updatedev", {shell = "uv build --wheel"}] [tool.pytest.ini_options] addopts = "-v --cov --cov-report=term --cov-report=xml --junitxml=junit.xml" junit_family = "legacy" testpaths = ["tests"] markers = [ - "slow: marks tests as slow (deselect with '-m \"not slow\"')" + "slow: marks tests as slow (deselect with '-m \"not slow\"')", ] [tool.coverage.run] omit = [ "tests/*", "*/__init__.py", - "*/_version.py", ] [tool.coverage.report] +show_missing = true +skip_covered = false exclude_lines = [ "pragma: no cover", "def __repr__", @@ -90,8 +96,6 @@ exclude_lines = [ "class .*\\bProtocol\\):", "@(abc\\.)?abstractmethod", ] -show_missing = true -skip_covered = false [tool.black] line-length = 120 @@ -99,22 +103,18 @@ skip-string-normalization = true [tool.ruff] line-length = 120 +target-version = "py311" [tool.ruff.lint] -# I - Import sorting and organization -# F401 - Detect and remove unused imports -select = ["I", "F401"] +select = ["E", "W", "F", "I", "B", "C4", "UP"] +ignore = ["E501", "E402", "UP046", "UP047"] + +[tool.ruff.lint.per-file-ignores] +"__init__.py" = ["F401"] +"tests/**/*.py" = ["S101", "S105", "S106", "S311", "SLF001", "F841"] [tool.ruff.lint.isort] known-first-party = ["pythonLogs"] force-sort-within-sections = false from-first = false no-sections = true - -[tool.ruff.lint.per-file-ignores] -# S101 Use of `assert` detected -# S105 Possible hardcoded password assigned to variable -# S106 Possible hardcoded password assigned to argument -# S311 Standard pseudo-random generators are not suitable for cryptographic purposes -# SLF001 Private member accessed -"tests/**/*.py" = ["S101", "S105", "S106", "S311", "SLF001"] diff --git a/pythonLogs/.env.example b/pythonLogs/.env.example index 12ba113..42657fc 100644 --- a/pythonLogs/.env.example +++ b/pythonLogs/.env.example @@ -15,6 +15,7 @@ LOG_STREAM_HANDLER=True LOG_SHOW_LOCATION=False # Memory Management Settings LOG_MAX_LOGGERS=50 +LOG_MAX_FORMATTERS=50 LOG_LOGGER_TTL_SECONDS=1800 # SizeRotatingLog Settings (only needed when using SizeRotatingLog) diff --git a/pythonLogs/core/constants.py b/pythonLogs/core/constants.py index 0edb131..3b5b0b1 100644 --- a/pythonLogs/core/constants.py +++ b/pythonLogs/core/constants.py @@ -1,5 +1,5 @@ import logging -from enum import Enum +from enum import StrEnum # File and Directory Constants MB_TO_BYTES = 1024 * 1024 @@ -17,7 +17,7 @@ DEFAULT_TIMEZONE = "UTC" -class LogLevel(str, Enum): +class LogLevel(StrEnum): """Log levels""" CRITICAL = "CRITICAL" @@ -29,7 +29,7 @@ class LogLevel(str, Enum): DEBUG = "DEBUG" -class RotateWhen(str, Enum): +class RotateWhen(StrEnum): """Rotation timing options for TimedRotatingLog""" MIDNIGHT = "midnight" diff --git a/pythonLogs/core/factory.py b/pythonLogs/core/factory.py index 267422c..ac58230 100644 --- a/pythonLogs/core/factory.py +++ b/pythonLogs/core/factory.py @@ -3,37 +3,37 @@ import threading import time from dataclasses import dataclass -from enum import Enum +from enum import StrEnum from pythonLogs.basic_log import BasicLog as _BasicLogImpl from pythonLogs.core.constants import LogLevel, RotateWhen from pythonLogs.core.log_utils import cleanup_logger_handlers from pythonLogs.core.settings import get_log_settings from pythonLogs.size_rotating import SizeRotatingLog as _SizeRotatingLogImpl from pythonLogs.timed_rotating import TimedRotatingLog as _TimedRotatingLogImpl -from typing import Dict, Optional, Tuple, Union, assert_never +from typing import assert_never @dataclass class LoggerConfig: """Configuration class to group logger parameters""" - level: Optional[Union[LogLevel, str]] = None - name: Optional[str] = None - directory: Optional[str] = None - filenames: Optional[list | tuple] = None - encoding: Optional[str] = None - datefmt: Optional[str] = None - timezone: Optional[str] = None - streamhandler: Optional[bool] = None - showlocation: Optional[bool] = None - maxmbytes: Optional[int] = None - when: Optional[Union[RotateWhen, str]] = None - sufix: Optional[str] = None - rotateatutc: Optional[bool] = None - daystokeep: Optional[int] = None - - -class LoggerType(str, Enum): + level: LogLevel | str | None = None + name: str | None = None + directory: str | None = None + filenames: list | tuple | None = None + encoding: str | None = None + datefmt: str | None = None + timezone: str | None = None + streamhandler: bool | None = None + showlocation: bool | None = None + maxmbytes: int | None = None + when: RotateWhen | str | None = None + sufix: str | None = None + rotateatutc: bool | None = None + daystokeep: int | None = None + + +class LoggerType(StrEnum): """Available logger types""" BASIC = "basic" @@ -45,7 +45,7 @@ class LoggerFactory: """Factory for creating different types of loggers with optimized instantiation and memory management""" # Logger registry for reusing loggers by name with timestamp tracking - _logger_registry: Dict[str, Tuple[logging.Logger, float]] = {} + _logger_registry: dict[str, tuple[logging.Logger, float]] = {} # Thread lock for registry access _registry_lock = threading.RLock() # Memory optimization settings @@ -71,8 +71,8 @@ def _ensure_initialized(cls) -> None: @classmethod def get_or_create_logger( cls, - logger_type: Union[LoggerType, str], - name: Optional[str] = None, + logger_type: LoggerType | str, + name: str | None = None, **kwargs, ) -> logging.Logger: """ @@ -218,9 +218,7 @@ def get_memory_limits(cls) -> dict[str, int]: return {'max_loggers': cls._max_loggers, 'ttl_seconds': cls._logger_ttl} @staticmethod - def create_logger( - logger_type: Union[LoggerType, str], config: Optional[LoggerConfig] = None, **kwargs - ) -> logging.Logger: + def create_logger(logger_type: LoggerType | str, config: LoggerConfig | None = None, **kwargs) -> logging.Logger: """ Factory method to create loggers based on type. @@ -239,8 +237,10 @@ def create_logger( if isinstance(logger_type, str): try: logger_type = LoggerType(logger_type.lower()) - except ValueError: - raise ValueError(f"Invalid logger type: {logger_type}. Valid types: {[t.value for t in LoggerType]}") + except ValueError as err: + raise ValueError( + f"Invalid logger type: {logger_type}. Valid types: {[t.value for t in LoggerType]}" + ) from err # Merge config and kwargs (kwargs take precedence for backward compatibility) if config is None: @@ -314,12 +314,12 @@ def create_logger( @staticmethod def create_basic_logger( - level: Optional[Union[LogLevel, str]] = None, - name: Optional[str] = None, - encoding: Optional[str] = None, - datefmt: Optional[str] = None, - timezone: Optional[str] = None, - showlocation: Optional[bool] = None, + level: LogLevel | str | None = None, + name: str | None = None, + encoding: str | None = None, + datefmt: str | None = None, + timezone: str | None = None, + showlocation: bool | None = None, ) -> logging.Logger: """Convenience method for creating a basic logger""" return LoggerFactory.create_logger( @@ -334,17 +334,17 @@ def create_basic_logger( @staticmethod def create_size_rotating_logger( - level: Optional[Union[LogLevel, str]] = None, - name: Optional[str] = None, - directory: Optional[str] = None, - filenames: Optional[list | tuple] = None, - maxmbytes: Optional[int] = None, - daystokeep: Optional[int] = None, - encoding: Optional[str] = None, - datefmt: Optional[str] = None, - timezone: Optional[str] = None, - streamhandler: Optional[bool] = None, - showlocation: Optional[bool] = None, + level: LogLevel | str | None = None, + name: str | None = None, + directory: str | None = None, + filenames: list | tuple | None = None, + maxmbytes: int | None = None, + daystokeep: int | None = None, + encoding: str | None = None, + datefmt: str | None = None, + timezone: str | None = None, + streamhandler: bool | None = None, + showlocation: bool | None = None, ) -> logging.Logger: """Convenience method for creating a size rotating logger""" return LoggerFactory.create_logger( @@ -364,19 +364,19 @@ def create_size_rotating_logger( @staticmethod def create_timed_rotating_logger( - level: Optional[Union[LogLevel, str]] = None, - name: Optional[str] = None, - directory: Optional[str] = None, - filenames: Optional[list | tuple] = None, - when: Optional[Union[RotateWhen, str]] = None, - sufix: Optional[str] = None, - daystokeep: Optional[int] = None, - encoding: Optional[str] = None, - datefmt: Optional[str] = None, - timezone: Optional[str] = None, - streamhandler: Optional[bool] = None, - showlocation: Optional[bool] = None, - rotateatutc: Optional[bool] = None, + level: LogLevel | str | None = None, + name: str | None = None, + directory: str | None = None, + filenames: list | tuple | None = None, + when: RotateWhen | str | None = None, + sufix: str | None = None, + daystokeep: int | None = None, + encoding: str | None = None, + datefmt: str | None = None, + timezone: str | None = None, + streamhandler: bool | None = None, + showlocation: bool | None = None, + rotateatutc: bool | None = None, ) -> logging.Logger: """Convenience method for creating a timed rotating logger""" return LoggerFactory.create_logger( @@ -432,12 +432,12 @@ class BasicLog(_LoggerMixin): def __init__( self, - level: Optional[Union[LogLevel, str]] = None, - name: Optional[str] = None, - encoding: Optional[str] = None, - datefmt: Optional[str] = None, - timezone: Optional[str] = None, - showlocation: Optional[bool] = None, + level: LogLevel | str | None = None, + name: str | None = None, + encoding: str | None = None, + datefmt: str | None = None, + timezone: str | None = None, + showlocation: bool | None = None, ): self._logger = LoggerFactory.create_basic_logger( level=level, @@ -465,17 +465,17 @@ class SizeRotatingLog(_LoggerMixin): def __init__( self, - level: Optional[Union[LogLevel, str]] = None, - name: Optional[str] = None, - directory: Optional[str] = None, - filenames: Optional[list | tuple] = None, - maxmbytes: Optional[int] = None, - daystokeep: Optional[int] = None, - encoding: Optional[str] = None, - datefmt: Optional[str] = None, - timezone: Optional[str] = None, - streamhandler: Optional[bool] = None, - showlocation: Optional[bool] = None, + level: LogLevel | str | None = None, + name: str | None = None, + directory: str | None = None, + filenames: list | tuple | None = None, + maxmbytes: int | None = None, + daystokeep: int | None = None, + encoding: str | None = None, + datefmt: str | None = None, + timezone: str | None = None, + streamhandler: bool | None = None, + showlocation: bool | None = None, ): self._logger = LoggerFactory.create_size_rotating_logger( level=level, @@ -508,19 +508,19 @@ class TimedRotatingLog(_LoggerMixin): def __init__( self, - level: Optional[Union[LogLevel, str]] = None, - name: Optional[str] = None, - directory: Optional[str] = None, - filenames: Optional[list | tuple] = None, - when: Optional[Union[RotateWhen, str]] = None, - sufix: Optional[str] = None, - daystokeep: Optional[int] = None, - encoding: Optional[str] = None, - datefmt: Optional[str] = None, - timezone: Optional[str] = None, - streamhandler: Optional[bool] = None, - showlocation: Optional[bool] = None, - rotateatutc: Optional[bool] = None, + level: LogLevel | str | None = None, + name: str | None = None, + directory: str | None = None, + filenames: list | tuple | None = None, + when: RotateWhen | str | None = None, + sufix: str | None = None, + daystokeep: int | None = None, + encoding: str | None = None, + datefmt: str | None = None, + timezone: str | None = None, + streamhandler: bool | None = None, + showlocation: bool | None = None, + rotateatutc: bool | None = None, ): self._logger = LoggerFactory.create_timed_rotating_logger( level=level, diff --git a/pythonLogs/core/log_utils.py b/pythonLogs/core/log_utils.py index 8dc1240..8be11ca 100644 --- a/pythonLogs/core/log_utils.py +++ b/pythonLogs/core/log_utils.py @@ -7,12 +7,11 @@ import sys import threading import time -from datetime import datetime, timedelta -from datetime import timezone as dttz +from collections.abc import Callable +from datetime import UTC, datetime, timedelta from functools import lru_cache from pathlib import Path from pythonLogs.core.constants import DEFAULT_FILE_MODE, LEVEL_MAP -from typing import Callable, Optional, Set from zoneinfo import ZoneInfo @@ -21,6 +20,8 @@ class RotatingLogMixin: logger: logging.Logger | None + def init(self) -> None: ... + def __enter__(self): """Context manager entry.""" if not hasattr(self, 'logger') or self.logger is None: @@ -39,7 +40,7 @@ def cleanup_logger(logger: logging.Logger) -> None: # Global cache for checked directories with thread safety and size limits -_checked_directories: Set[str] = set() +_checked_directories: set[str] = set() _directory_lock = threading.Lock() _max_cached_directories = 500 # Limit cache size to prevent unbounded growth @@ -109,7 +110,7 @@ def check_directory_permissions(directory_path: str) -> None: except PermissionError as e: err_msg = f"Unable to create directory | {directory_path}" write_stderr(f"{err_msg} | {type(e).__name__}: {e}") - raise PermissionError(err_msg) + raise PermissionError(err_msg) from e # Add to cache with size limit enforcement if len(_checked_directories) >= _max_cached_directories: @@ -129,7 +130,7 @@ def remove_old_logs(logs_dir: str, days_to_keep: int) -> None: try: if file_path.stat().st_mtime < cutoff_time.timestamp(): file_path.unlink() - except (OSError, IOError) as e: + except OSError as e: write_stderr(f"Unable to delete old log | {file_path} | {type(e).__name__}: {e}") except OSError as e: write_stderr(f"Unable to scan directory for old logs | {logs_dir} | {type(e).__name__}: {e}") @@ -196,7 +197,7 @@ def write_stderr(msg: str) -> None: # Use local timezone dt = datetime.now() else: - dt = datetime.now(dttz.utc).astimezone(tz) + dt = datetime.now(UTC).astimezone(tz) dt_timezone = dt.strftime("%Y-%m-%dT%H:%M:%S.%f%z") sys.stderr.write(f"[{dt_timezone}]:[ERROR]:{msg}\n") except (OSError, ValueError, KeyError): @@ -286,7 +287,7 @@ def gzip_file_with_sufix(file_path: str, sufix: str) -> str | None: # Final attempt failed or not Windows - treat as regular error write_stderr(f"Unable to gzip log file | {file_path} | {type(e).__name__}: {e}") raise e - except (OSError, IOError) as e: + except OSError as e: write_stderr(f"Unable to gzip log file | {file_path} | {type(e).__name__}: {e}") raise e @@ -324,7 +325,7 @@ def get_timezone_function(time_zone: str) -> Callable: # Shared handler cleanup utility -def cleanup_logger_handlers(logger: Optional[logging.Logger]) -> None: +def cleanup_logger_handlers(logger: logging.Logger | None) -> None: """Clean up logger resources by closing all handlers. This is a centralized utility to ensure consistent cleanup behavior diff --git a/pythonLogs/core/memory_utils.py b/pythonLogs/core/memory_utils.py index a0f6a1b..9473b16 100644 --- a/pythonLogs/core/memory_utils.py +++ b/pythonLogs/core/memory_utils.py @@ -2,16 +2,17 @@ import threading import weakref from . import log_utils +from .settings import get_log_settings from functools import lru_cache -from typing import Any, Dict, Optional, Set +from typing import Any # Formatter cache to reduce memory usage for identical formatters -_formatter_cache: Dict[str, logging.Formatter] = {} +_formatter_cache: dict[str, logging.Formatter] = {} _formatter_cache_lock = threading.Lock() -_max_formatters = 50 # Limit formatter cache size +_max_formatters = get_log_settings().max_formatters -def get_cached_formatter(format_string: str, datefmt: Optional[str] = None) -> logging.Formatter: +def get_cached_formatter(format_string: str, datefmt: str | None = None) -> logging.Formatter: """Get a cached formatter or create and cache a new one. This reduces memory usage by reusing formatter instances with @@ -66,7 +67,7 @@ def clear_directory_cache() -> None: # Weak reference registry for tracking active loggers without preventing GC -_active_loggers: Set[weakref.ReferenceType] = set() +_active_loggers: set[weakref.ReferenceType] = set() _weak_ref_lock = threading.Lock() @@ -103,7 +104,7 @@ def get_active_logger_count() -> int: return len(_active_loggers) -def get_memory_stats() -> Dict[str, Any]: +def get_memory_stats() -> dict[str, Any]: """Get memory usage statistics for the logging system. Returns: @@ -153,7 +154,7 @@ def optimize_lru_cache_sizes() -> None: log_utils.get_stderr_timezone = lru_cache(maxsize=4)(log_utils.get_stderr_timezone.__wrapped__) -def force_garbage_collection() -> Dict[str, int]: +def force_garbage_collection() -> dict[str, int]: """Force garbage collection and return collection statistics. This can be useful for testing memory leaks or forcing cleanup diff --git a/pythonLogs/core/settings.py b/pythonLogs/core/settings.py index 405836e..af9ea0a 100644 --- a/pythonLogs/core/settings.py +++ b/pythonLogs/core/settings.py @@ -75,6 +75,10 @@ class LogSettings(BaseSettings): default=100, description="Maximum number of loggers to track in memory", ) + max_formatters: int = Field( + default=50, + description="Maximum number of formatters to cache in memory", + ) logger_ttl_seconds: int = Field( default=3600, description="Time-to-live in seconds for logger references", diff --git a/pythonLogs/core/thread_safety.py b/pythonLogs/core/thread_safety.py index 3f98df9..ccc5c84 100644 --- a/pythonLogs/core/thread_safety.py +++ b/pythonLogs/core/thread_safety.py @@ -1,6 +1,7 @@ import functools import threading -from typing import Any, Callable, Dict, Type, TypeVar +from collections.abc import Callable +from typing import Any, TypeVar F = TypeVar('F', bound=Callable[..., Any]) @@ -8,7 +9,7 @@ class ThreadSafeMeta(type): """Metaclass that automatically adds thread safety to class methods.""" - def __new__(mcs, name: str, bases: tuple, namespace: Dict[str, Any], **kwargs): + def __new__(mcs, name: str, bases: tuple, namespace: dict[str, Any], **kwargs): # Create the class first cls = super().__new__(mcs, name, bases, namespace) @@ -51,8 +52,8 @@ def wrapper(self, *args, **kwargs): if lock is None: # Check if class has lock, if not create one if not hasattr(self.__class__, '_lock'): - setattr(self.__class__, '_lock', threading.RLock()) - lock = getattr(self.__class__, '_lock') + self.__class__._lock = threading.RLock() + lock = self.__class__._lock with lock: return func(self, *args, **kwargs) @@ -60,7 +61,7 @@ def wrapper(self, *args, **kwargs): return wrapper -def _get_wrappable_methods(cls: Type) -> list: +def _get_wrappable_methods(cls: type) -> list: """Helper function to get methods that should be made thread-safe.""" return [ method_name @@ -73,13 +74,13 @@ def _get_wrappable_methods(cls: Type) -> list: ] -def _ensure_class_has_lock(cls: Type) -> None: +def _ensure_class_has_lock(cls: type) -> None: """Ensure the class has a lock attribute.""" if not hasattr(cls, '_lock'): cls._lock = threading.RLock() -def _should_wrap_method(cls: Type, method_name: str, original_method: Any) -> bool: +def _should_wrap_method(cls: type, method_name: str, original_method: Any) -> bool: """Check if a method should be wrapped with thread safety.""" return ( hasattr(cls, method_name) and callable(original_method) and not hasattr(original_method, '_thread_safe_wrapped') @@ -89,7 +90,7 @@ def _should_wrap_method(cls: Type, method_name: str, original_method: Any) -> bo def auto_thread_safe(thread_safe_methods: list = None): """Class decorator that adds automatic thread safety to specified methods.""" - def decorator(cls: Type) -> Type: + def decorator(cls: type) -> type: _ensure_class_has_lock(cls) # Store thread-safe methods list diff --git a/tests/core/test_basic_log.py b/tests/core/test_basic_log.py index 3f772a7..a2ae15f 100644 --- a/tests/core/test_basic_log.py +++ b/tests/core/test_basic_log.py @@ -178,7 +178,7 @@ def test_basic_log_multiple_instances(self): assert logger.level == logging.INFO # Clean up all loggers - for basic_log, logger in loggers: + for _basic_log, logger in loggers: BasicLog.cleanup_logger(logger) assert len(logger.handlers) == 0 diff --git a/tests/core/test_log_utils.py b/tests/core/test_log_utils.py index 4b9479d..abdba42 100644 --- a/tests/core/test_log_utils.py +++ b/tests/core/test_log_utils.py @@ -385,7 +385,7 @@ def test_check_directory_permissions(self): try: os.makedirs(directory, mode=0, exist_ok=True) # No permissions at all - assert os.path.exists(directory) == True + assert os.path.exists(directory) with pytest.raises(PermissionError) as exec_info: log_utils.check_directory_permissions(directory) assert type(exec_info.value) is PermissionError @@ -419,7 +419,7 @@ def test_check_directory_permissions(self): def test_remove_old_logs(self): directory = os.path.join(tempfile.gettempdir(), "test_remove_logs") os.makedirs(directory, mode=0o755, exist_ok=True) - assert os.path.exists(directory) == True + assert os.path.exists(directory) # Create a file and manually set its modification time to be old with tempfile.NamedTemporaryFile(dir=directory, suffix=".gz", delete=False) as tmpfile: @@ -428,9 +428,9 @@ def test_remove_old_logs(self): os.utime(file_path, (old_time, old_time)) log_utils.remove_old_logs(directory, 1) # Remove files older than 1 day - assert os.path.isfile(file_path) == False + assert not os.path.isfile(file_path) log_utils.delete_file(directory) - assert os.path.exists(directory) == False + assert not os.path.exists(directory) def test_delete_file(self): """Test delete_file with standard Unix/Linux file handling.""" @@ -438,9 +438,9 @@ def test_delete_file(self): file_path = tmp_file.name tmp_file.write("test content") - assert os.path.isfile(file_path) == True + assert os.path.isfile(file_path) log_utils.delete_file(file_path) - assert os.path.isfile(file_path) == False + assert not os.path.isfile(file_path) def test_is_older_than_x_days(self): """Test is_older_than_x_days with standard Unix/Linux file handling.""" @@ -449,18 +449,18 @@ def test_is_older_than_x_days(self): tmp_file.write("test content") try: - assert os.path.isfile(file_path) == True + assert os.path.isfile(file_path) # When days=1, it compares against 1 day ago, so newly created file should NOT be older result = log_utils.is_older_than_x_days(file_path, 1) - assert result == False + assert not result # When days=5, it compares against 5 days ago, so newly created file should NOT be older result = log_utils.is_older_than_x_days(file_path, 5) - assert result == False + assert not result log_utils.delete_file(file_path) - assert os.path.isfile(file_path) == False + assert not os.path.isfile(file_path) finally: # Ensure cleanup if the test fails if os.path.exists(file_path): @@ -564,7 +564,7 @@ def test_gzip_file_with_sufix(self): tmp_file.write("test content for gzip") try: - assert os.path.isfile(file_path) == True + assert os.path.isfile(file_path) sufix = "test1" result = log_utils.gzip_file_with_sufix(file_path, sufix) file_path_no_suffix = file_path.split(".")[0] @@ -573,7 +573,7 @@ def test_gzip_file_with_sufix(self): # Clean up the gzipped file if os.path.exists(result): os.unlink(result) - assert os.path.isfile(result) == False + assert not os.path.isfile(result) finally: # Ensure cleanup of the original file if it still exists @@ -747,7 +747,7 @@ def test_is_older_than_x_days_edge_cases(self): time.sleep(0.001) # 1ms delay to handle Windows timing precision result = log_utils.is_older_than_x_days(tmp_file.name, 0) - assert result == True # Should use current time as cutoff + assert result # Should use current time as cutoff # Test with non-existent file with pytest.raises(FileNotFoundError): @@ -920,7 +920,7 @@ def test_delete_file_special_file(self): # delete_file should handle symlinks result = log_utils.delete_file(link_file) - assert result == True + assert result assert not os.path.exists(link_file) @pytest.mark.skipif(sys.platform == "win32", reason="Unix/Linux/macOS-specific chmod test") @@ -1199,7 +1199,7 @@ def test_cache_eviction_stress_test(self): finally: # Cleanup using context managers - for temp_dir, temp_dir_context in temp_dirs: + for _temp_dir, temp_dir_context in temp_dirs: temp_dir_context.__exit__(None, None, None) log_utils._max_cached_directories = original_max @@ -1356,7 +1356,7 @@ def test_delete_file_special_file_coverage(self): # delete_file should handle this special file result = log_utils.delete_file(fifo_path) - assert result == True + assert result assert not os.path.exists(fifo_path) except OSError: # FIFO creation might not be supported on all systems @@ -1577,7 +1577,7 @@ def test_gzip_file_ioerror_handling(self): def mock_copyfileobj(*args, **kwargs): # Raise IOError to trigger lines 265-267 - raise IOError("Mock IOError during file copy") + raise OSError("Mock IOError during file copy") try: with unittest.mock.patch('shutil.copyfileobj', side_effect=mock_copyfileobj): diff --git a/tests/core/test_log_utils_windows.py b/tests/core/test_log_utils_windows.py index 4622776..2005f9a 100644 --- a/tests/core/test_log_utils_windows.py +++ b/tests/core/test_log_utils_windows.py @@ -92,9 +92,9 @@ def test_delete_file_windows_safe(self): file_handle.write("test content") file_handle.close() - assert os.path.isfile(file_path) == True + assert os.path.isfile(file_path) log_utils.delete_file(file_path) - assert os.path.isfile(file_path) == False + assert not os.path.isfile(file_path) finally: # Ensure cleanup if the test fails if os.path.exists(file_path): @@ -113,18 +113,18 @@ def test_is_older_than_x_days_windows_safe(self): file_handle.write("test content") file_handle.close() - assert os.path.isfile(file_path) == True + assert os.path.isfile(file_path) # When days=1, it compares against 1 day ago, so newly created file should NOT be older result = log_utils.is_older_than_x_days(file_path, 1) - assert result == False + assert not result # When days=5, it compares against 5 days ago, so newly created file should NOT be older result = log_utils.is_older_than_x_days(file_path, 5) - assert result == False + assert not result log_utils.delete_file(file_path) - assert os.path.isfile(file_path) == False + assert not os.path.isfile(file_path) finally: # Ensure cleanup if the test fails if os.path.exists(file_path): @@ -141,7 +141,7 @@ def test_gzip_file_with_sufix_windows_safe(self): file_handle.write("test content for gzip") file_handle.close() - assert os.path.isfile(file_path) == True + assert os.path.isfile(file_path) sufix = "test1" result = log_utils.gzip_file_with_sufix(file_path, sufix) file_path_no_suffix = file_path.split(".")[0] @@ -149,7 +149,7 @@ def test_gzip_file_with_sufix_windows_safe(self): # Clean up the gzipped file with Windows-safe deletion safe_close_and_delete_file(None, result) - assert os.path.isfile(result) == False + assert not os.path.isfile(result) finally: # Ensure cleanup of the original file if it still exists @@ -399,7 +399,7 @@ def windows_file_worker(worker_id): # Verify all workers completed successfully for result in results: - assert result['is_old'] == False # Files should NOT be considered "old" (created recently) + assert not result['is_old'] # Files should NOT be considered "old" (created recently) assert result['gzip_result'] is not None assert f"worker_{result['worker_id']}" in result['gzip_result'] @@ -556,7 +556,7 @@ def test_windows_delete_file_scenarios(self): assert os.path.isfile(file_path) result = log_utils.delete_file(file_path) - assert result == True + assert result assert not os.path.exists(file_path) finally: if os.path.exists(file_path): @@ -597,7 +597,7 @@ def test_windows_logger_cleanup_integration(self): for log_file in temp_files: if os.path.exists(log_file): result = safe_delete_file(log_file) - assert result == True + assert result finally: # Ensure cleanup cleanup_all_loggers() @@ -630,7 +630,7 @@ def test_windows_directory_cleanup_stress(self): # Windows-safe cleanup should handle this result = safe_delete_directory(nested_dir) - assert result == True or not os.path.exists(nested_dir) + assert result or not os.path.exists(nested_dir) finally: # Final cleanup cleanup_all_loggers() diff --git a/tests/core/test_settings.py b/tests/core/test_settings.py index 8f83635..2f830d8 100644 --- a/tests/core/test_settings.py +++ b/tests/core/test_settings.py @@ -37,6 +37,7 @@ def test_default_values(self): assert settings.stream_handler is True assert settings.show_location is False assert settings.max_loggers == 100 + assert settings.max_formatters == 50 assert settings.logger_ttl_seconds == 3600 assert settings.max_file_size_mb == 10 assert settings.rotate_when.value == "midnight" @@ -67,12 +68,20 @@ def test_custom_values(self): timezone="UTC", appname="custom_app", max_loggers=50, + max_formatters=25, ) assert settings.level == LogLevel.DEBUG assert settings.timezone == "UTC" assert settings.appname == "custom_app" assert settings.max_loggers == 50 + assert settings.max_formatters == 25 + + def test_max_formatters_env_var(self): + """Test that LOG_MAX_FORMATTERS env var overrides default.""" + with patch.dict(os.environ, {"LOG_MAX_FORMATTERS": "75"}): + settings = LogSettings() + assert settings.max_formatters == 75 class TestGetLogSettings: diff --git a/tests/factory/test_factory.py b/tests/factory/test_factory.py index 02d8521..48ecbc4 100644 --- a/tests/factory/test_factory.py +++ b/tests/factory/test_factory.py @@ -95,7 +95,7 @@ def test_performance_improvement_with_caching(self): # Test with registry (reuses loggers) clear_logger_registry() start_time = time.time() - for i in range(20): + for _i in range(20): LoggerFactory.get_or_create_logger(LoggerType.BASIC, name="cached_perf_test") cached_time = time.time() - start_time diff --git a/tests/performance/test_memory_optimization.py b/tests/performance/test_memory_optimization.py index 24b9c5a..f26ebf5 100644 --- a/tests/performance/test_memory_optimization.py +++ b/tests/performance/test_memory_optimization.py @@ -112,7 +112,7 @@ def test_directory_cache_size_limit(self): finally: # Cleanup temp directories using context managers - for temp_dir, temp_dir_context in temp_dirs: + for _temp_dir, temp_dir_context in temp_dirs: temp_dir_context.__exit__(None, None, None) def test_formatter_cache_efficiency(self): @@ -494,7 +494,7 @@ def test_set_directory_cache_limit_edge_cases(self): finally: # Cleanup using context managers - for temp_dir, temp_dir_context in temp_dirs: + for _temp_dir, temp_dir_context in temp_dirs: temp_dir_context.__exit__(None, None, None) def test_register_logger_weakref_direct(self): diff --git a/tests/performance/test_performance.py b/tests/performance/test_performance.py index 1a4dc63..053ca75 100644 --- a/tests/performance/test_performance.py +++ b/tests/performance/test_performance.py @@ -65,7 +65,7 @@ def test_registry_caching_performance(self): # With caching: Reuse same logger clear_logger_registry() start_time = time.time() - for i in range(30): + for _i in range(30): LoggerFactory.get_or_create_logger(LoggerType.BASIC, name="cached_logger") cache_time = time.time() - start_time diff --git a/tests/thread_safety/test_automatic_features.py b/tests/thread_safety/test_automatic_features.py index 7728a29..9986e75 100644 --- a/tests/thread_safety/test_automatic_features.py +++ b/tests/thread_safety/test_automatic_features.py @@ -30,7 +30,7 @@ def test_logger_operations(): # Run in multiple threads to test thread safety threads = [] - for i in range(5): + for _i in range(5): thread = threading.Thread(target=test_logger_operations) threads.append(thread) thread.start() @@ -70,7 +70,7 @@ def test_logger_operations(): # Run in multiple threads to test thread safety threads = [] - for i in range(3): + for _i in range(3): thread = threading.Thread(target=test_logger_operations) threads.append(thread) thread.start() @@ -106,7 +106,7 @@ def test_logger_operations(): # Run in multiple threads to test thread safety threads = [] - for i in range(3): + for _i in range(3): thread = threading.Thread(target=test_logger_operations) threads.append(thread) thread.start() diff --git a/tests/thread_safety/test_thread_safety.py b/tests/thread_safety/test_thread_safety.py index d8f62cf..898aba3 100644 --- a/tests/thread_safety/test_thread_safety.py +++ b/tests/thread_safety/test_thread_safety.py @@ -361,7 +361,7 @@ def _create_logger_and_messages(self, worker_id, temp_dir, results_lock, thread_ log_file = os.path.join(temp_dir, f"independent_{worker_id}.log") assert os.path.exists(log_file), f"Log file missing for thread {worker_id}" - with open(log_file, 'r') as f: + with open(log_file) as f: _log_content = f.read() # Verify all messages are in the file diff --git a/uv.lock b/uv.lock index d810b85..c6cb5e5 100644 --- a/uv.lock +++ b/uv.lock @@ -1,6 +1,6 @@ version = 1 revision = 3 -requires-python = ">=3.12" +requires-python = ">=3.11" [[package]] name = "annotated-types" @@ -25,6 +25,11 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/13/88/560b11e521c522440af991d46848a2bde64b5f7202ec14e1f46f9509d328/black-26.1.0.tar.gz", hash = "sha256:d294ac3340eef9c9eb5d29288e96dc719ff269a88e27b396340459dd85da4c58", size = 658785, upload-time = "2026-01-18T04:50:11.993Z" } wheels = [ + { url = "https://files.pythonhosted.org/packages/30/83/f05f22ff13756e1a8ce7891db517dbc06200796a16326258268f4658a745/black-26.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3cee1487a9e4c640dc7467aaa543d6c0097c391dc8ac74eb313f2fbf9d7a7cb5", size = 1831956, upload-time = "2026-01-18T04:59:21.38Z" }, + { url = "https://files.pythonhosted.org/packages/7d/f2/b2c570550e39bedc157715e43927360312d6dd677eed2cc149a802577491/black-26.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d62d14ca31c92adf561ebb2e5f2741bf8dea28aef6deb400d49cca011d186c68", size = 1672499, upload-time = "2026-01-18T04:59:23.257Z" }, + { url = "https://files.pythonhosted.org/packages/7a/d7/990d6a94dc9e169f61374b1c3d4f4dd3037e93c2cc12b6f3b12bc663aa7b/black-26.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb1dafbbaa3b1ee8b4550a84425aac8874e5f390200f5502cf3aee4a2acb2f14", size = 1735431, upload-time = "2026-01-18T04:59:24.729Z" }, + { url = "https://files.pythonhosted.org/packages/36/1c/cbd7bae7dd3cb315dfe6eeca802bb56662cc92b89af272e014d98c1f2286/black-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:101540cb2a77c680f4f80e628ae98bd2bd8812fb9d72ade4f8995c5ff019e82c", size = 1400468, upload-time = "2026-01-18T04:59:27.381Z" }, + { url = "https://files.pythonhosted.org/packages/59/b1/9fe6132bb2d0d1f7094613320b56297a108ae19ecf3041d9678aec381b37/black-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:6f3977a16e347f1b115662be07daa93137259c711e526402aa444d7a88fdc9d4", size = 1207332, upload-time = "2026-01-18T04:59:28.711Z" }, { url = "https://files.pythonhosted.org/packages/f5/13/710298938a61f0f54cdb4d1c0baeb672c01ff0358712eddaf29f76d32a0b/black-26.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6eeca41e70b5f5c84f2f913af857cf2ce17410847e1d54642e658e078da6544f", size = 1878189, upload-time = "2026-01-18T04:59:30.682Z" }, { url = "https://files.pythonhosted.org/packages/79/a6/5179beaa57e5dbd2ec9f1c64016214057b4265647c62125aa6aeffb05392/black-26.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd39eef053e58e60204f2cdf059e2442e2eb08f15989eefe259870f89614c8b6", size = 1700178, upload-time = "2026-01-18T04:59:32.387Z" }, { url = "https://files.pythonhosted.org/packages/8c/04/c96f79d7b93e8f09d9298b333ca0d31cd9b2ee6c46c274fd0f531de9dc61/black-26.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9459ad0d6cd483eacad4c6566b0f8e42af5e8b583cee917d90ffaa3778420a0a", size = 1777029, upload-time = "2026-01-18T04:59:33.767Z" }, @@ -70,6 +75,19 @@ version = "7.13.3" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/11/43/3e4ac666cc35f231fa70c94e9f38459299de1a152813f9d2f60fc5f3ecaf/coverage-7.13.3.tar.gz", hash = "sha256:f7f6182d3dfb8802c1747eacbfe611b669455b69b7c037484bb1efbbb56711ac", size = 826832, upload-time = "2026-02-03T14:02:30.944Z" } wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/09/1ac74e37cf45f17eb41e11a21854f7f92a4c2d6c6098ef4a1becb0c6d8d3/coverage-7.13.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5907605ee20e126eeee2abe14aae137043c2c8af2fa9b38d2ab3b7a6b8137f73", size = 219276, upload-time = "2026-02-03T14:00:00.296Z" }, + { url = "https://files.pythonhosted.org/packages/2e/cb/71908b08b21beb2c437d0d5870c4ec129c570ca1b386a8427fcdb11cf89c/coverage-7.13.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a88705500988c8acad8b8fd86c2a933d3aa96bec1ddc4bc5cb256360db7bbd00", size = 219776, upload-time = "2026-02-03T14:00:02.414Z" }, + { url = "https://files.pythonhosted.org/packages/09/85/c4f3dd69232887666a2c0394d4be21c60ea934d404db068e6c96aa59cd87/coverage-7.13.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bbb5aa9016c4c29e3432e087aa29ebee3f8fda089cfbfb4e6d64bd292dcd1c2", size = 250196, upload-time = "2026-02-03T14:00:04.197Z" }, + { url = "https://files.pythonhosted.org/packages/9c/cc/560ad6f12010344d0778e268df5ba9aa990aacccc310d478bf82bf3d302c/coverage-7.13.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0c2be202a83dde768937a61cdc5d06bf9fb204048ca199d93479488e6247656c", size = 252111, upload-time = "2026-02-03T14:00:05.639Z" }, + { url = "https://files.pythonhosted.org/packages/f0/66/3193985fb2c58e91f94cfbe9e21a6fdf941e9301fe2be9e92c072e9c8f8c/coverage-7.13.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f45e32ef383ce56e0ca099b2e02fcdf7950be4b1b56afaab27b4ad790befe5b", size = 254217, upload-time = "2026-02-03T14:00:07.738Z" }, + { url = "https://files.pythonhosted.org/packages/c5/78/f0f91556bf1faa416792e537c523c5ef9db9b1d32a50572c102b3d7c45b3/coverage-7.13.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6ed2e787249b922a93cd95c671cc9f4c9797a106e81b455c83a9ddb9d34590c0", size = 250318, upload-time = "2026-02-03T14:00:09.224Z" }, + { url = "https://files.pythonhosted.org/packages/6f/aa/fc654e45e837d137b2c1f3a2cc09b4aea1e8b015acd2f774fa0f3d2ddeba/coverage-7.13.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:05dd25b21afffe545e808265897c35f32d3e4437663923e0d256d9ab5031fb14", size = 251909, upload-time = "2026-02-03T14:00:10.712Z" }, + { url = "https://files.pythonhosted.org/packages/73/4d/ab53063992add8a9ca0463c9d92cce5994a29e17affd1c2daa091b922a93/coverage-7.13.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:46d29926349b5c4f1ea4fca95e8c892835515f3600995a383fa9a923b5739ea4", size = 249971, upload-time = "2026-02-03T14:00:12.402Z" }, + { url = "https://files.pythonhosted.org/packages/29/25/83694b81e46fcff9899694a1b6f57573429cdd82b57932f09a698f03eea5/coverage-7.13.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:fae6a21537519c2af00245e834e5bf2884699cc7c1055738fd0f9dc37a3644ad", size = 249692, upload-time = "2026-02-03T14:00:13.868Z" }, + { url = "https://files.pythonhosted.org/packages/d4/ef/d68fc304301f4cb4bf6aefa0045310520789ca38dabdfba9dbecd3f37919/coverage-7.13.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c672d4e2f0575a4ca2bf2aa0c5ced5188220ab806c1bb6d7179f70a11a017222", size = 250597, upload-time = "2026-02-03T14:00:15.461Z" }, + { url = "https://files.pythonhosted.org/packages/8d/85/240ad396f914df361d0f71e912ddcedb48130c71b88dc4193fe3c0306f00/coverage-7.13.3-cp311-cp311-win32.whl", hash = "sha256:fcda51c918c7a13ad93b5f89a58d56e3a072c9e0ba5c231b0ed81404bf2648fb", size = 221773, upload-time = "2026-02-03T14:00:17.462Z" }, + { url = "https://files.pythonhosted.org/packages/2f/71/165b3a6d3d052704a9ab52d11ea64ef3426745de517dda44d872716213a7/coverage-7.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:d1a049b5c51b3b679928dd35e47c4a2235e0b6128b479a7596d0ef5b42fa6301", size = 222711, upload-time = "2026-02-03T14:00:19.449Z" }, + { url = "https://files.pythonhosted.org/packages/51/d0/0ddc9c5934cdd52639c5df1f1eb0fdab51bb52348f3a8d1c7db9c600d93a/coverage-7.13.3-cp311-cp311-win_arm64.whl", hash = "sha256:79f2670c7e772f4917895c3d89aad59e01f3dbe68a4ed2d0373b431fad1dcfba", size = 221377, upload-time = "2026-02-03T14:00:20.968Z" }, { url = "https://files.pythonhosted.org/packages/94/44/330f8e83b143f6668778ed61d17ece9dc48459e9e74669177de02f45fec5/coverage-7.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ed48b4170caa2c4420e0cd27dc977caaffc7eecc317355751df8373dddcef595", size = 219441, upload-time = "2026-02-03T14:00:22.585Z" }, { url = "https://files.pythonhosted.org/packages/08/e7/29db05693562c2e65bdf6910c0af2fd6f9325b8f43caf7a258413f369e30/coverage-7.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8f2adf4bcffbbec41f366f2e6dffb9d24e8172d16e91da5799c9b7ed6b5716e6", size = 219801, upload-time = "2026-02-03T14:00:24.186Z" }, { url = "https://files.pythonhosted.org/packages/90/ae/7f8a78249b02b0818db46220795f8ac8312ea4abd1d37d79ea81db5cae81/coverage-7.13.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:01119735c690786b6966a1e9f098da4cd7ca9174c4cfe076d04e653105488395", size = 251306, upload-time = "2026-02-03T14:00:25.798Z" }, @@ -138,6 +156,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7d/fb/70af542d2d938c778c9373ce253aa4116dbe7c0a5672f78b2b2ae0e1b94b/coverage-7.13.3-py3-none-any.whl", hash = "sha256:90a8af9dba6429b2573199622d72e0ebf024d6276f16abce394ad4d181bb0910", size = 211237, upload-time = "2026-02-03T14:02:27.986Z" }, ] +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + [[package]] name = "iniconfig" version = "2.3.0" @@ -266,6 +289,20 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" }, + { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" }, + { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" }, + { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" }, + { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" }, + { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" }, + { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" }, + { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" }, + { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" }, + { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" }, { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, @@ -322,10 +359,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, + { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" }, + { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" }, + { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" }, { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, + { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" }, + { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" }, + { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" }, + { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" }, + { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" }, + { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" }, ] [[package]] @@ -372,7 +421,7 @@ name = "pytest-cov" version = "7.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "coverage" }, + { name = "coverage", extra = ["toml"] }, { name = "pluggy" }, { name = "pytest" }, ] @@ -408,12 +457,6 @@ dev = [ { name = "pytest-cov" }, { name = "ruff" }, ] -test = [ - { name = "coverage" }, - { name = "psutil" }, - { name = "pytest" }, - { name = "pytest-cov" }, -] [package.metadata] requires-dist = [{ name = "pydantic-settings", specifier = ">=2.11.0" }] @@ -428,12 +471,6 @@ dev = [ { name = "pytest-cov", specifier = ">=7.0.0" }, { name = "ruff", specifier = ">=0.15.0" }, ] -test = [ - { name = "coverage", specifier = ">=7.13.3" }, - { name = "psutil", specifier = ">=7.2.2" }, - { name = "pytest", specifier = ">=9.0.2" }, - { name = "pytest-cov", specifier = ">=7.0.0" }, -] [[package]] name = "pytokens" @@ -441,6 +478,11 @@ version = "0.4.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/92/790ebe03f07b57e53b10884c329b9a1a308648fc083a6d4a39a10a28c8fc/pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440", size = 160864, upload-time = "2026-01-30T01:02:57.882Z" }, + { url = "https://files.pythonhosted.org/packages/13/25/a4f555281d975bfdd1eba731450e2fe3a95870274da73fb12c40aeae7625/pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc", size = 248565, upload-time = "2026-01-30T01:02:59.912Z" }, + { url = "https://files.pythonhosted.org/packages/17/50/bc0394b4ad5b1601be22fa43652173d47e4c9efbf0044c62e9a59b747c56/pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d", size = 260824, upload-time = "2026-01-30T01:03:01.471Z" }, + { url = "https://files.pythonhosted.org/packages/4e/54/3e04f9d92a4be4fc6c80016bc396b923d2a6933ae94b5f557c939c460ee0/pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16", size = 264075, upload-time = "2026-01-30T01:03:04.143Z" }, + { url = "https://files.pythonhosted.org/packages/d1/1b/44b0326cb5470a4375f37988aea5d61b5cc52407143303015ebee94abfd6/pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6", size = 103323, upload-time = "2026-01-30T01:03:05.412Z" }, { url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" }, { url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" }, { url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" }, @@ -470,6 +512,15 @@ version = "6.0.3" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, @@ -535,6 +586,60 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f6/b0/2d823f6e77ebe560f4e397d078487e8d52c1516b331e3521bc75db4272ca/ruff-0.15.0-py3-none-win_arm64.whl", hash = "sha256:c480d632cc0ca3f0727acac8b7d053542d9e114a462a145d0b00e7cd658c515a", size = 10865753, upload-time = "2026-02-03T17:53:03.014Z" }, ] +[[package]] +name = "tomli" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" }, + { url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" }, + { url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" }, + { url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" }, + { url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" }, + { url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" }, + { url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" }, + { url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" }, + { url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" }, + { url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" }, + { url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" }, + { url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" }, + { url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" }, + { url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" }, + { url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" }, + { url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" }, + { url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" }, + { url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" }, + { url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" }, + { url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" }, + { url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" }, + { url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" }, + { url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" }, + { url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" }, + { url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" }, + { url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" }, + { url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" }, + { url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" }, + { url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" }, + { url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" }, + { url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" }, + { url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" }, + { url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" }, + { url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" }, + { url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" }, + { url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" }, + { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" }, +] + [[package]] name = "typing-extensions" version = "4.15.0" From 975e8ba00cfbe50995f32ecc43b140c1acceed62 Mon Sep 17 00:00:00 2001 From: ddc Date: Thu, 5 Feb 2026 16:30:21 -0300 Subject: [PATCH 6/9] V6.0.2 --- .github/workflows/workflow.yml | 6 +- pythonLogs/basic_log.py | 6 +- pythonLogs/core/factory.py | 30 ++--- pythonLogs/core/log_utils.py | 4 +- pythonLogs/core/memory_utils.py | 20 ++-- pythonLogs/core/thread_safety.py | 30 ++--- pythonLogs/size_rotating.py | 2 +- pythonLogs/timed_rotating.py | 2 +- .../test_context_managers.py | 12 +- .../test_resource_management.py | 2 +- tests/core/test_basic_log.py | 14 +-- tests/core/test_log_utils.py | 26 ++--- tests/core/test_log_utils_windows.py | 34 +++--- tests/factory/test_enums.py | 6 +- tests/factory/test_factory.py | 8 +- tests/logger_types/test_size_rotating.py | 4 +- tests/logger_types/test_timed_rotating.py | 10 +- tests/performance/test_memory_optimization.py | 104 +++++++++--------- tests/performance/test_performance.py | 5 +- tests/performance/test_performance_windows.py | 4 +- .../performance/test_performance_zoneinfo.py | 2 +- .../thread_safety/test_automatic_features.py | 10 +- .../test_automatic_thread_safety.py | 4 +- tests/thread_safety/test_thread_safety.py | 36 +++--- .../test_thread_safety_module.py | 50 ++++----- .../test_thread_safety_patterns.py | 32 +++--- tests/timezone/test_timezone_migration.py | 4 +- tests/timezone/test_zoneinfo_fallbacks.py | 16 +-- 28 files changed, 243 insertions(+), 240 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 657afe1..555f2a1 100755 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -17,13 +17,11 @@ jobs: - uses: astral-sh/ruff-action@v3 with: args: "check" - - uses: astral-sh/ruff-action@v3 - with: - args: "format --check" test: name: Test Python ${{ matrix.python-version }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} + needs: lint strategy: fail-fast: false matrix: @@ -66,7 +64,7 @@ jobs: build: name: Build Package runs-on: ubuntu-latest - needs: [lint, test] + needs: test if: startsWith(github.ref, 'refs/tags/v') steps: - uses: actions/checkout@v6 diff --git a/pythonLogs/basic_log.py b/pythonLogs/basic_log.py index 5ad3231..55e52b4 100644 --- a/pythonLogs/basic_log.py +++ b/pythonLogs/basic_log.py @@ -5,7 +5,7 @@ from pythonLogs.core.thread_safety import auto_thread_safe -@auto_thread_safe(['init']) +@auto_thread_safe(["init"]) class BasicLog: """Basic logger with context manager support for automatic resource cleanup.""" @@ -48,13 +48,13 @@ def init(self): def __enter__(self): """Context manager entry.""" - if not hasattr(self, 'logger') or self.logger is None: + if not hasattr(self, "logger") or self.logger is None: self.init() return self.logger def __exit__(self, exc_type, exc_val, exc_tb): """Context manager exit with automatic cleanup.""" - if hasattr(self, 'logger'): + if hasattr(self, "logger"): cleanup_logger_handlers(self.logger) @staticmethod diff --git a/pythonLogs/core/factory.py b/pythonLogs/core/factory.py index ac58230..cf8bc3b 100644 --- a/pythonLogs/core/factory.py +++ b/pythonLogs/core/factory.py @@ -215,7 +215,7 @@ def get_memory_limits(cls) -> dict[str, int]: Dictionary with current max_loggers and ttl_seconds settings """ with cls._registry_lock: - return {'max_loggers': cls._max_loggers, 'ttl_seconds': cls._logger_ttl} + return {"max_loggers": cls._max_loggers, "ttl_seconds": cls._logger_ttl} @staticmethod def create_logger(logger_type: LoggerType | str, config: LoggerConfig | None = None, **kwargs) -> logging.Logger: @@ -248,20 +248,20 @@ def create_logger(logger_type: LoggerType | str, config: LoggerConfig | None = N # Create a new config with kwargs overriding config values final_config = LoggerConfig( - level=kwargs.get('level', config.level), - name=kwargs.get('name', config.name), - directory=kwargs.get('directory', config.directory), - filenames=kwargs.get('filenames', config.filenames), - encoding=kwargs.get('encoding', config.encoding), - datefmt=kwargs.get('datefmt', config.datefmt), - timezone=kwargs.get('timezone', config.timezone), - streamhandler=kwargs.get('streamhandler', config.streamhandler), - showlocation=kwargs.get('showlocation', config.showlocation), - maxmbytes=kwargs.get('maxmbytes', config.maxmbytes), - when=kwargs.get('when', config.when), - sufix=kwargs.get('sufix', config.sufix), - rotateatutc=kwargs.get('rotateatutc', config.rotateatutc), - daystokeep=kwargs.get('daystokeep', config.daystokeep), + level=kwargs.get("level", config.level), + name=kwargs.get("name", config.name), + directory=kwargs.get("directory", config.directory), + filenames=kwargs.get("filenames", config.filenames), + encoding=kwargs.get("encoding", config.encoding), + datefmt=kwargs.get("datefmt", config.datefmt), + timezone=kwargs.get("timezone", config.timezone), + streamhandler=kwargs.get("streamhandler", config.streamhandler), + showlocation=kwargs.get("showlocation", config.showlocation), + maxmbytes=kwargs.get("maxmbytes", config.maxmbytes), + when=kwargs.get("when", config.when), + sufix=kwargs.get("sufix", config.sufix), + rotateatutc=kwargs.get("rotateatutc", config.rotateatutc), + daystokeep=kwargs.get("daystokeep", config.daystokeep), ) # Convert enum values to strings for logger classes diff --git a/pythonLogs/core/log_utils.py b/pythonLogs/core/log_utils.py index 8be11ca..af6479b 100644 --- a/pythonLogs/core/log_utils.py +++ b/pythonLogs/core/log_utils.py @@ -24,13 +24,13 @@ def init(self) -> None: ... def __enter__(self): """Context manager entry.""" - if not hasattr(self, 'logger') or self.logger is None: + if not hasattr(self, "logger") or self.logger is None: self.init() return self.logger def __exit__(self, exc_type, exc_val, exc_tb): """Context manager exit with automatic cleanup.""" - if hasattr(self, 'logger'): + if hasattr(self, "logger"): cleanup_logger_handlers(self.logger) @staticmethod diff --git a/pythonLogs/core/memory_utils.py b/pythonLogs/core/memory_utils.py index 9473b16..2df3a9b 100644 --- a/pythonLogs/core/memory_utils.py +++ b/pythonLogs/core/memory_utils.py @@ -126,13 +126,13 @@ def get_memory_stats() -> dict[str, Any]: directory_stats = log_utils.get_directory_cache_stats() return { - 'registry_size': registry_size, - 'formatter_cache_size': formatter_cache_size, - 'directory_cache_size': directory_stats['cached_directories'], - 'active_logger_count': get_active_logger_count(), - 'max_registry_size': factory_limits['max_loggers'], - 'max_formatter_cache': _max_formatters, - 'max_directory_cache': directory_stats['max_directories'], + "registry_size": registry_size, + "formatter_cache_size": formatter_cache_size, + "directory_cache_size": directory_stats["cached_directories"], + "active_logger_count": get_active_logger_count(), + "max_registry_size": factory_limits["max_loggers"], + "max_formatter_cache": _max_formatters, + "max_directory_cache": directory_stats["max_directories"], } @@ -173,7 +173,7 @@ def force_garbage_collection() -> dict[str, int]: collected = gc.collect() return { - 'objects_collected': collected, - 'garbage_count': len(gc.garbage), - 'reference_cycles': gc.get_count(), + "objects_collected": collected, + "garbage_count": len(gc.garbage), + "reference_cycles": gc.get_count(), } diff --git a/pythonLogs/core/thread_safety.py b/pythonLogs/core/thread_safety.py index ccc5c84..e8f2031 100644 --- a/pythonLogs/core/thread_safety.py +++ b/pythonLogs/core/thread_safety.py @@ -3,7 +3,7 @@ from collections.abc import Callable from typing import Any, TypeVar -F = TypeVar('F', bound=Callable[..., Any]) +F = TypeVar("F", bound=Callable[..., Any]) class ThreadSafeMeta(type): @@ -14,11 +14,11 @@ def __new__(mcs, name: str, bases: tuple, namespace: dict[str, Any], **kwargs): cls = super().__new__(mcs, name, bases, namespace) # Add a class-level lock if not already present - if not hasattr(cls, '_lock'): + if not hasattr(cls, "_lock"): cls._lock = threading.RLock() # Get methods that should be thread-safe (exclude private/dunder methods) - thread_safe_methods = getattr(cls, '_thread_safe_methods', None) + thread_safe_methods = getattr(cls, "_thread_safe_methods", None) if thread_safe_methods is None: # Auto-detect public methods that modify state thread_safe_methods = [ @@ -26,8 +26,8 @@ def __new__(mcs, name: str, bases: tuple, namespace: dict[str, Any], **kwargs): for method_name in namespace if ( callable(getattr(cls, method_name, None)) - and not method_name.startswith('_') - and method_name not in ['__enter__', '__exit__', '__init__'] + and not method_name.startswith("_") + and method_name not in ["__enter__", "__exit__", "__init__"] ) ] @@ -48,10 +48,10 @@ def thread_safe(func: F) -> F: @functools.wraps(func) def wrapper(self, *args, **kwargs): # Use instance lock if available, otherwise class lock - lock = getattr(self, '_lock', None) + lock = getattr(self, "_lock", None) if lock is None: # Check if class has lock, if not create one - if not hasattr(self.__class__, '_lock'): + if not hasattr(self.__class__, "_lock"): self.__class__._lock = threading.RLock() lock = self.__class__._lock @@ -68,22 +68,22 @@ def _get_wrappable_methods(cls: type) -> list: for method_name in dir(cls) if ( callable(getattr(cls, method_name, None)) - and not method_name.startswith('_') - and method_name not in ['__enter__', '__exit__', '__init__'] + and not method_name.startswith("_") + and method_name not in ["__enter__", "__exit__", "__init__"] ) ] def _ensure_class_has_lock(cls: type) -> None: """Ensure the class has a lock attribute.""" - if not hasattr(cls, '_lock'): + if not hasattr(cls, "_lock"): cls._lock = threading.RLock() def _should_wrap_method(cls: type, method_name: str, original_method: Any) -> bool: """Check if a method should be wrapped with thread safety.""" return ( - hasattr(cls, method_name) and callable(original_method) and not hasattr(original_method, '_thread_safe_wrapped') + hasattr(cls, method_name) and callable(original_method) and not hasattr(original_method, "_thread_safe_wrapped") ) @@ -117,21 +117,21 @@ class AutoThreadSafe: """Base class that provides automatic thread safety for all public methods.""" def __init__(self): - if not hasattr(self, '_lock'): + if not hasattr(self, "_lock"): self._lock = threading.RLock() def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) # Add class-level lock - if not hasattr(cls, '_lock'): + if not hasattr(cls, "_lock"): cls._lock = threading.RLock() # Auto-wrap public methods for attr_name in dir(cls): - if not attr_name.startswith('_'): + if not attr_name.startswith("_"): attr = getattr(cls, attr_name) - if callable(attr) and not hasattr(attr, '_thread_safe_wrapped'): + if callable(attr) and not hasattr(attr, "_thread_safe_wrapped"): wrapped_attr = thread_safe(attr) wrapped_attr._thread_safe_wrapped = True setattr(cls, attr_name, wrapped_attr) diff --git a/pythonLogs/size_rotating.py b/pythonLogs/size_rotating.py index 6e7e42e..361e3e2 100755 --- a/pythonLogs/size_rotating.py +++ b/pythonLogs/size_rotating.py @@ -20,7 +20,7 @@ from pythonLogs.core.thread_safety import auto_thread_safe -@auto_thread_safe(['init']) +@auto_thread_safe(["init"]) class SizeRotatingLog(RotatingLogMixin): """Size-based rotating logger with context manager support for automatic resource cleanup.""" diff --git a/pythonLogs/timed_rotating.py b/pythonLogs/timed_rotating.py index 9ae6fb3..ac2298f 100755 --- a/pythonLogs/timed_rotating.py +++ b/pythonLogs/timed_rotating.py @@ -16,7 +16,7 @@ from pythonLogs.core.thread_safety import auto_thread_safe -@auto_thread_safe(['init']) +@auto_thread_safe(["init"]) class TimedRotatingLog(RotatingLogMixin): """ Time-based rotating logger with context manager support for automatic resource cleanup. diff --git a/tests/context_management/test_context_managers.py b/tests/context_management/test_context_managers.py index b1be75a..e5f5cc6 100644 --- a/tests/context_management/test_context_managers.py +++ b/tests/context_management/test_context_managers.py @@ -71,7 +71,7 @@ def test_size_rotating_context_manager(self): assert logger.level == logging.DEBUG # Should have file handlers - file_handlers = [h for h in logger.handlers if hasattr(h, 'baseFilename')] + file_handlers = [h for h in logger.handlers if hasattr(h, "baseFilename")] assert len(file_handlers) > 0 # Test logging @@ -98,7 +98,7 @@ def test_timed_rotating_context_manager(self): assert logger.level == logging.WARNING # Should have file handlers - file_handlers = [h for h in logger.handlers if hasattr(h, 'baseFilename')] + file_handlers = [h for h in logger.handlers if hasattr(h, "baseFilename")] assert len(file_handlers) > 0 # Test logging @@ -134,7 +134,7 @@ def test_context_manager_without_init(self): with logger_instance as logger: # Context manager should have called init() - assert hasattr(logger_instance, 'logger') + assert hasattr(logger_instance, "logger") assert logger_instance.logger is not None assert isinstance(logger, logging.Logger) logger.info("Test message") @@ -148,7 +148,7 @@ def test_context_manager_with_existing_init(self): # Call init() manually first manual_logger = logger_instance.init() - assert hasattr(logger_instance, 'logger') + assert hasattr(logger_instance, "logger") with logger_instance as context_logger: # Should return the same logger @@ -167,7 +167,7 @@ def test_multiple_file_handlers_cleanup(self): name=logger_name, directory=self.temp_dir, filenames=multiple_files, maxmbytes=1 ) as logger: # Should have multiple file handlers - file_handlers = [h for h in logger.handlers if hasattr(h, 'baseFilename')] + file_handlers = [h for h in logger.handlers if hasattr(h, "baseFilename")] assert len(file_handlers) == len(multiple_files) logger.info("Test message to multiple files") @@ -188,7 +188,7 @@ def test_stream_handler_cleanup(self): ) as logger: # Should have both file and stream handlers stream_handlers = [h for h in logger.handlers if isinstance(h, logging.StreamHandler)] - file_handlers = [h for h in logger.handlers if hasattr(h, 'baseFilename')] + file_handlers = [h for h in logger.handlers if hasattr(h, "baseFilename")] assert len(stream_handlers) > 0 assert len(file_handlers) > 0 diff --git a/tests/context_management/test_resource_management.py b/tests/context_management/test_resource_management.py index fc00bc5..5d94c25 100644 --- a/tests/context_management/test_resource_management.py +++ b/tests/context_management/test_resource_management.py @@ -165,7 +165,7 @@ def test_registry_clear_with_file_handlers(self): logger.info("Test message before cleanup") # Verify we have multiple handlers - file_handlers = [h for h in logger.handlers if hasattr(h, 'baseFilename')] + file_handlers = [h for h in logger.handlers if hasattr(h, "baseFilename")] stream_handlers = [h for h in logger.handlers if isinstance(h, logging.StreamHandler)] assert len(file_handlers) == 2 # Two file handlers diff --git a/tests/core/test_basic_log.py b/tests/core/test_basic_log.py index a2ae15f..11b1b1b 100644 --- a/tests/core/test_basic_log.py +++ b/tests/core/test_basic_log.py @@ -27,12 +27,12 @@ def teardown_method(self): def test_basic_log_initialization(self): """Test BasicLog initialization with default parameters.""" basic_log = BasicLog() - assert hasattr(basic_log, 'level') - assert hasattr(basic_log, 'appname') - assert hasattr(basic_log, 'encoding') - assert hasattr(basic_log, 'datefmt') - assert hasattr(basic_log, 'timezone') - assert hasattr(basic_log, 'showlocation') + assert hasattr(basic_log, "level") + assert hasattr(basic_log, "appname") + assert hasattr(basic_log, "encoding") + assert hasattr(basic_log, "datefmt") + assert hasattr(basic_log, "timezone") + assert hasattr(basic_log, "showlocation") assert basic_log.logger is None def test_basic_log_initialization_with_params(self): @@ -60,7 +60,7 @@ def test_basic_log_init_method(self): assert isinstance(logger, logging.Logger) assert logger.name == "test_init" assert logger.level == logging.INFO - assert hasattr(basic_log, 'logger') + assert hasattr(basic_log, "logger") assert basic_log.logger is logger def test_basic_log_logger_functionality(self): diff --git a/tests/core/test_log_utils.py b/tests/core/test_log_utils.py index abdba42..877414b 100644 --- a/tests/core/test_log_utils.py +++ b/tests/core/test_log_utils.py @@ -80,11 +80,11 @@ def patch_logger_kwargs_with_safe_timezone(kwargs): """Patch logger kwargs to use safe timezone if UTC is specified but not available.""" from zoneinfo import ZoneInfo - if kwargs.get('timezone') == 'UTC': + if kwargs.get("timezone") == "UTC": try: ZoneInfo("UTC") # Test if UTC timezone data is available except KeyError: - kwargs['timezone'] = 'localtime' # Fall back to localtime if UTC data is missing + kwargs["timezone"] = "localtime" # Fall back to localtime if UTC data is missing return kwargs @@ -333,7 +333,7 @@ def create_windows_safe_temp_file(suffix="", prefix="tmp", dir=None, text=False) fd, filepath = tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=dir, text=text) # Convert file descriptor to file handle - mode = 'w' if text else 'wb' + mode = "w" if text else "wb" file_handle = os.fdopen(fd, mode) return file_handle, filepath @@ -550,12 +550,12 @@ def test_get_format(self): import re # The % characters need to be literal in the regex - offset_pattern = r'\[%\(asctime\)s\.%\(msecs\)03d([+-]\d{4})\]' + offset_pattern = r"\[%\(asctime\)s\.%\(msecs\)03d([+-]\d{4})\]" match = re.search(offset_pattern, result) assert match is not None, f"No timezone offset found in format: {result}" # The offset could be +1000 (if timezone is available) or system localtime fallback offset = match.group(1) - assert re.match(r'[+-]\d{4}', offset), f"Invalid timezone offset format: {offset}" + assert re.match(r"[+-]\d{4}", offset), f"Invalid timezone offset format: {offset}" def test_gzip_file_with_sufix(self): """Test gzip_file_with_sufix with standard Unix/Linux file handling.""" @@ -893,7 +893,7 @@ def mock_glob(self, pattern): return original_path_glob(self, pattern) try: - with unittest.mock.patch.object(Path, 'glob', mock_glob): + with unittest.mock.patch.object(Path, "glob", mock_glob): stderr_capture = io.StringIO() with contextlib.redirect_stderr(stderr_capture): log_utils.remove_old_logs(test_dir, 1) @@ -1250,7 +1250,7 @@ def test_timezone_offset_various_timezones(self): offset = log_utils.get_timezone_offset(tz) assert isinstance(offset, str) assert len(offset) == 5 # Format: +/-HHMM - assert offset[0] in ['+', '-'] + assert offset[0] in ["+", "-"] if expected_offset: assert offset == expected_offset @@ -1443,7 +1443,7 @@ def test_timezone_offset_fallback_exception(self): # Should fall back to localtime (lines 216-219) assert isinstance(result, str) assert len(result) == 5 # Format: +/-HHMM - assert result[0] in ['+', '-'] + assert result[0] in ["+", "-"] def test_gzip_file_source_deletion_error_coverage(self): """Test gzip_file_with_sufix when source file deletion fails.""" @@ -1467,7 +1467,7 @@ def mock_unlink(self): try: stderr_capture = io.StringIO() with contextlib.redirect_stderr(stderr_capture): - with unittest.mock.patch.object(Path, 'unlink', mock_unlink): + with unittest.mock.patch.object(Path, "unlink", mock_unlink): with pytest.raises(OSError): log_utils.gzip_file_with_sufix(test_file, "test") @@ -1494,7 +1494,7 @@ def mock_zoneinfo(key): return ZoneInfo(key) try: - with unittest.mock.patch('pythonLogs.core.log_utils.ZoneInfo', side_effect=mock_zoneinfo): + with unittest.mock.patch("pythonLogs.core.log_utils.ZoneInfo", side_effect=mock_zoneinfo): result = log_utils.get_timezone_function("UTC") # Should fall back to localtime (lines 273-275) @@ -1519,7 +1519,7 @@ def mock_zoneinfo(key): return ZoneInfo(key) try: - with unittest.mock.patch('pythonLogs.core.log_utils.ZoneInfo', side_effect=mock_zoneinfo): + with unittest.mock.patch("pythonLogs.core.log_utils.ZoneInfo", side_effect=mock_zoneinfo): result = log_utils.get_timezone_function("Custom/Timezone") # Should fall back to localtime (lines 283-285) @@ -1544,7 +1544,7 @@ def mock_gzip_open(*args, **kwargs): raise OSError("Mock OSError during gzip compression") try: - with unittest.mock.patch('gzip.open', side_effect=mock_gzip_open): + with unittest.mock.patch("gzip.open", side_effect=mock_gzip_open): stderr_capture = io.StringIO() with contextlib.redirect_stderr(stderr_capture): with pytest.raises(OSError) as exc_info: @@ -1580,7 +1580,7 @@ def mock_copyfileobj(*args, **kwargs): raise OSError("Mock IOError during file copy") try: - with unittest.mock.patch('shutil.copyfileobj', side_effect=mock_copyfileobj): + with unittest.mock.patch("shutil.copyfileobj", side_effect=mock_copyfileobj): stderr_capture = io.StringIO() with contextlib.redirect_stderr(stderr_capture): with pytest.raises(IOError) as exc_info: diff --git a/tests/core/test_log_utils_windows.py b/tests/core/test_log_utils_windows.py index 2005f9a..1f8b90a 100644 --- a/tests/core/test_log_utils_windows.py +++ b/tests/core/test_log_utils_windows.py @@ -49,7 +49,7 @@ def test_check_directory_permissions_windows(self): # Test with a path that contains invalid characters (Windows-specific) try: - invalid_chars_path = os.path.join(temp_dir, "invalid<>:|*?\"path") + invalid_chars_path = os.path.join(temp_dir, 'invalid<>:|*?"path') # This might raise different exceptions on different Windows versions with pytest.raises((OSError, ValueError)) as exec_info: log_utils.check_directory_permissions(invalid_chars_path) @@ -170,7 +170,7 @@ def test_gzip_file_windows_retry_mechanism(self): file_handle.close() # Mock time.sleep to verify retry mechanism - with patch('pythonLogs.core.log_utils.time.sleep') as mock_sleep: + with patch("pythonLogs.core.log_utils.time.sleep") as mock_sleep: # Mock open to raise PermissionError on first call, succeed on second call_count = 0 original_open = open @@ -186,8 +186,8 @@ def mock_open_side_effect(*args, real_open=original_open, **kwargs): return real_open(*args, **kwargs) # Always mock platform as win32 and open with retry behavior - with patch('pythonLogs.core.log_utils.sys.platform', 'win32'): - with patch('pythonLogs.core.log_utils.open', side_effect=mock_open_side_effect): + with patch("pythonLogs.core.log_utils.sys.platform", "win32"): + with patch("pythonLogs.core.log_utils.open", side_effect=mock_open_side_effect): result = log_utils.gzip_file_with_sufix(file_path, "retry_test") # Verify retry was attempted (sleep was called) @@ -225,12 +225,12 @@ def test_timezone_fallback_windows(self): import re # The % characters need to be literal in the regex - offset_pattern = r'\[%\(asctime\)s\.%\(msecs\)03d([+-]\d{4})\]' + offset_pattern = r"\[%\(asctime\)s\.%\(msecs\)03d([+-]\d{4})\]" match = re.search(offset_pattern, result) assert match is not None, f"No timezone offset found in format: {result}" # The offset could be the specific timezone or system localtime fallback offset = match.group(1) - assert re.match(r'[+-]\d{4}', offset), f"Invalid timezone offset format: {offset}" + assert re.match(r"[+-]\d{4}", offset), f"Invalid timezone offset format: {offset}" @pytest.mark.skipif(sys.platform != "win32", reason="Windows-specific tests") def test_windows_timezone_environment_fallback(self): @@ -327,7 +327,7 @@ def test_windows_stderr_timezone_with_dst(self): # Should contain some form of timezone offset # Windows may fall back to local timezone if specific timezone unavailable - assert any(char in output for char in ['+', '-']) or 'Z' in output + assert any(char in output for char in ["+", "-"]) or "Z" in output finally: if original_tz is not None: @@ -366,10 +366,10 @@ def windows_file_worker(worker_id): with lock: results.append( { - 'worker_id': worker_id, - 'file_path': file_path, - 'is_old': is_old, - 'gzip_result': gzip_result, + "worker_id": worker_id, + "file_path": file_path, + "is_old": is_old, + "gzip_result": gzip_result, } ) @@ -399,9 +399,9 @@ def windows_file_worker(worker_id): # Verify all workers completed successfully for result in results: - assert not result['is_old'] # Files should NOT be considered "old" (created recently) - assert result['gzip_result'] is not None - assert f"worker_{result['worker_id']}" in result['gzip_result'] + assert not result["is_old"] # Files should NOT be considered "old" (created recently) + assert result["gzip_result"] is not None + assert f"worker_{result['worker_id']}" in result["gzip_result"] @pytest.mark.skipif(sys.platform != "win32", reason="Windows-specific tests") def test_gzip_file_windows_permission_error(self): @@ -504,9 +504,9 @@ def mock_open_side_effect(*args, **kwargs): return original_open(*args, **kwargs) # Mock sys.platform to be Windows and time.sleep to verify retry - with unittest.mock.patch('pythonLogs.core.log_utils.sys.platform', 'win32'): - with unittest.mock.patch('pythonLogs.core.log_utils.time.sleep') as mock_sleep: - with unittest.mock.patch('pythonLogs.core.log_utils.open', side_effect=mock_open_side_effect): + with unittest.mock.patch("pythonLogs.core.log_utils.sys.platform", "win32"): + with unittest.mock.patch("pythonLogs.core.log_utils.time.sleep") as mock_sleep: + with unittest.mock.patch("pythonLogs.core.log_utils.open", side_effect=mock_open_side_effect): result = log_utils.gzip_file_with_sufix(file_path, "comprehensive_retry") # Verify retries were attempted (sleep should be called twice) diff --git a/tests/factory/test_enums.py b/tests/factory/test_enums.py index 80185d0..2eae93f 100644 --- a/tests/factory/test_enums.py +++ b/tests/factory/test_enums.py @@ -28,7 +28,9 @@ def setup_method(self): def test_log_level_enum_usage(self): """Test LogLevel enum usage.""" logger = LoggerFactory.create_logger( - LoggerType.BASIC, name="enum_test", level=LogLevel.DEBUG # Using enum instead of string + LoggerType.BASIC, + name="enum_test", + level=LogLevel.DEBUG, # Using enum instead of string ) assert logger.name == "enum_test" assert logger.level == 10 # DEBUG level @@ -96,7 +98,7 @@ def test_all_rotate_when_enum_values(self): ] # Just verify they're accessible and have expected values - expected_values = ['midnight', 'H', 'D', 'W0', 'W1', 'W2', 'W3', 'W4', 'W5', 'W6'] + expected_values = ["midnight", "H", "D", "W0", "W1", "W2", "W3", "W4", "W5", "W6"] actual_values = [when.value for when in when_options] assert actual_values == expected_values diff --git a/tests/factory/test_factory.py b/tests/factory/test_factory.py index 48ecbc4..68128c6 100644 --- a/tests/factory/test_factory.py +++ b/tests/factory/test_factory.py @@ -318,7 +318,7 @@ def test_factory_ensure_initialized_behavior(self): @pytest.mark.parametrize("error_type", [OSError, ValueError, RuntimeError]) def test_factory_atexit_cleanup_error_handling(self, error_type): """Test atexit cleanup handles expected exceptions gracefully.""" - with patch.object(LoggerFactory, 'clear_registry', side_effect=error_type("Test error")): + with patch.object(LoggerFactory, "clear_registry", side_effect=error_type("Test error")): # Should not raise an exception (silently ignored) LoggerFactory._atexit_cleanup() @@ -382,7 +382,7 @@ def test_factory_memory_limits_from_settings(self): mock_settings.logger_ttl_seconds = 1800 # Patch the import inside the function - with patch('pythonLogs.core.factory.get_log_settings', return_value=mock_settings): + with patch("pythonLogs.core.factory.get_log_settings", return_value=mock_settings): # Reset initialization flag LoggerFactory._initialized = False @@ -456,8 +456,8 @@ def test_factory_get_memory_limits(self): # Get and verify limits limits = LoggerFactory.get_memory_limits() - assert limits['max_loggers'] == 75 - assert limits['ttl_seconds'] == 2400 + assert limits["max_loggers"] == 75 + assert limits["ttl_seconds"] == 2400 @pytest.mark.skipif( sys.platform == "win32", diff --git a/tests/logger_types/test_size_rotating.py b/tests/logger_types/test_size_rotating.py index 57b478c..661c2cc 100644 --- a/tests/logger_types/test_size_rotating.py +++ b/tests/logger_types/test_size_rotating.py @@ -322,7 +322,7 @@ def test_gzip_rotator_size_call_with_nonexistent_source(self): gz_files = list(Path(temp_dir).glob("*.gz")) assert len(gz_files) == 0 - @patch('pythonLogs.size_rotating.remove_old_logs') + @patch("pythonLogs.size_rotating.remove_old_logs") @pytest.mark.skipif( sys.platform == "win32", reason="Windows file locking issues with TemporaryDirectory - see equivalent Windows-specific test file", @@ -365,7 +365,7 @@ def test_gzip_rotator_size_integration(self): # Force handlers to flush for handler in logger.handlers: - if hasattr(handler, 'flush'): + if hasattr(handler, "flush"): handler.flush() # Verify log file exists diff --git a/tests/logger_types/test_timed_rotating.py b/tests/logger_types/test_timed_rotating.py index 30387ea..873437f 100644 --- a/tests/logger_types/test_timed_rotating.py +++ b/tests/logger_types/test_timed_rotating.py @@ -323,7 +323,7 @@ def test_gzip_rotator_timed_suffix_extraction(self): # Reset source file source_file.write_text("Test content") - with patch('pythonLogs.timed_rotating.gzip_file_with_sufix') as mock_gzip: + with patch("pythonLogs.timed_rotating.gzip_file_with_sufix") as mock_gzip: rotator(str(source_file), dest_filename) mock_gzip.assert_called_once_with(str(source_file), expected_suffix) @@ -343,7 +343,7 @@ def test_gzip_rotator_timed_with_nonexistent_source(self): gz_files = list(Path(temp_dir).glob("*.gz")) assert len(gz_files) == 0 - @patch('pythonLogs.timed_rotating.remove_old_logs') + @patch("pythonLogs.timed_rotating.remove_old_logs") @pytest.mark.skipif( sys.platform == "win32", reason="Windows file locking issues with TemporaryDirectory - see equivalent Windows-specific test file", @@ -362,7 +362,7 @@ def test_gzip_rotator_timed_calls_remove_old_logs(self, mock_remove_old_logs): # Should have called remove_old_logs mock_remove_old_logs.assert_called_once_with(temp_dir, 7) - @patch('pythonLogs.timed_rotating.gzip_file_with_sufix') + @patch("pythonLogs.timed_rotating.gzip_file_with_sufix") @pytest.mark.skipif( sys.platform == "win32", reason="Windows file locking issues with TemporaryDirectory - see equivalent Windows-specific test file", @@ -405,7 +405,7 @@ def test_gzip_rotator_timed_integration(self): # Force handlers to flush for handler in logger.handlers: - if hasattr(handler, 'flush'): + if hasattr(handler, "flush"): handler.flush() # Verify log file exists @@ -431,7 +431,7 @@ def test_gzip_rotator_timed_suffix_edge_cases(self): for dest_filename, expected_suffix in edge_cases: source_file.write_text("Test content") # Reset file - with patch('pythonLogs.timed_rotating.gzip_file_with_sufix') as mock_gzip: + with patch("pythonLogs.timed_rotating.gzip_file_with_sufix") as mock_gzip: rotator(str(source_file), dest_filename) if dest_filename: # Only call if dest_filename is not empty mock_gzip.assert_called_once_with(str(source_file), expected_suffix) diff --git a/tests/performance/test_memory_optimization.py b/tests/performance/test_memory_optimization.py index f26ebf5..3337a76 100644 --- a/tests/performance/test_memory_optimization.py +++ b/tests/performance/test_memory_optimization.py @@ -26,7 +26,7 @@ ) -@pytest.mark.skipif(os.getenv('CI') == 'true', reason="Performance tests unstable in CI") +@pytest.mark.skipif(os.getenv("CI") == "true", reason="Performance tests unstable in CI") class TestMemoryOptimization: """Test cases for memory optimization features.""" @@ -151,22 +151,22 @@ def test_memory_stats_reporting(self): # Verify stats structure expected_keys = { - 'registry_size', - 'formatter_cache_size', - 'directory_cache_size', - 'active_logger_count', - 'max_registry_size', - 'max_formatter_cache', - 'max_directory_cache', + "registry_size", + "formatter_cache_size", + "directory_cache_size", + "active_logger_count", + "max_registry_size", + "max_formatter_cache", + "max_directory_cache", } assert set(stats.keys()) == expected_keys # Verify some basic constraints - assert stats['registry_size'] >= 3 - assert stats['max_registry_size'] > 0 - assert stats['max_formatter_cache'] > 0 - assert stats['max_directory_cache'] > 0 - assert isinstance(stats['active_logger_count'], int) + assert stats["registry_size"] >= 3 + assert stats["max_registry_size"] > 0 + assert stats["max_formatter_cache"] > 0 + assert stats["max_directory_cache"] > 0 + assert isinstance(stats["active_logger_count"], int) def test_weak_reference_tracking(self): """Test that weak references track active loggers correctly.""" @@ -228,7 +228,7 @@ def test_force_garbage_collection(self): gc_stats = force_garbage_collection() # Verify stats structure - expected_keys = {'objects_collected', 'garbage_count', 'reference_cycles'} + expected_keys = {"objects_collected", "garbage_count", "reference_cycles"} assert set(gc_stats.keys()) == expected_keys # Verify all values are integers @@ -268,10 +268,10 @@ def memory_worker(worker_id): results.append( { - 'worker_id': worker_id, - 'logger_name': logger.name, - 'stats': stats, - 'formatter': formatter is not None, + "worker_id": worker_id, + "logger_name": logger.name, + "stats": stats, + "formatter": formatter is not None, } ) @@ -291,7 +291,7 @@ def memory_worker(worker_id): # Verify memory constraints were maintained final_stats = get_memory_stats() - assert final_stats['registry_size'] <= 20 # Respect size limit + assert final_stats["registry_size"] <= 20 # Respect size limit def test_memory_leak_prevention(self): """Test that the library prevents common memory leaks.""" @@ -316,12 +316,12 @@ def test_memory_leak_prevention(self): final_stats = get_memory_stats() # The Registry should not have grown excessively - registry_growth = final_stats['registry_size'] - initial_stats['registry_size'] + registry_growth = final_stats["registry_size"] - initial_stats["registry_size"] assert registry_growth <= 20, f"Registry grew by {registry_growth}, possible memory leak" # Cache sizes should be reasonable - assert final_stats['formatter_cache_size'] <= 50 - assert final_stats['directory_cache_size'] <= 500 + assert final_stats["formatter_cache_size"] <= 50 + assert final_stats["directory_cache_size"] <= 500 @pytest.mark.skipif( sys.platform == "win32", @@ -639,13 +639,13 @@ def test_memory_stats_comprehensive(self): # Verify all required fields exist required_fields = [ - 'registry_size', - 'formatter_cache_size', - 'directory_cache_size', - 'active_logger_count', - 'max_registry_size', - 'max_formatter_cache', - 'max_directory_cache', + "registry_size", + "formatter_cache_size", + "directory_cache_size", + "active_logger_count", + "max_registry_size", + "max_formatter_cache", + "max_directory_cache", ] for field in required_fields: @@ -654,9 +654,9 @@ def test_memory_stats_comprehensive(self): assert stats[field] >= 0, f"Field {field} should be non-negative" # Verify relationships - assert stats['registry_size'] <= stats['max_registry_size'] - assert stats['formatter_cache_size'] <= stats['max_formatter_cache'] - assert stats['directory_cache_size'] <= stats['max_directory_cache'] + assert stats["registry_size"] <= stats["max_registry_size"] + assert stats["formatter_cache_size"] <= stats["max_formatter_cache"] + assert stats["directory_cache_size"] <= stats["max_directory_cache"] def test_force_garbage_collection_comprehensive(self): """Test comprehensive garbage collection functionality.""" @@ -665,13 +665,13 @@ def test_force_garbage_collection_comprehensive(self): # Create objects that could be garbage collected test_objects = [] for i in range(100): - test_objects.append({'data': f"test_data_{i}" * 100, 'nested': {'value': i, 'list': list(range(10))}}) + test_objects.append({"data": f"test_data_{i}" * 100, "nested": {"value": i, "list": list(range(10))}}) # Create circular references - obj1 = {'name': 'obj1'} - obj2 = {'name': 'obj2'} - obj1['ref'] = obj2 - obj2['ref'] = obj1 + obj1 = {"name": "obj1"} + obj2 = {"name": "obj2"} + obj1["ref"] = obj2 + obj2["ref"] = obj1 test_objects.extend([obj1, obj2]) # Clear references @@ -681,14 +681,14 @@ def test_force_garbage_collection_comprehensive(self): gc_stats = force_garbage_collection() # Verify stats - assert 'objects_collected' in gc_stats - assert 'garbage_count' in gc_stats - assert 'reference_cycles' in gc_stats + assert "objects_collected" in gc_stats + assert "garbage_count" in gc_stats + assert "reference_cycles" in gc_stats - assert isinstance(gc_stats['objects_collected'], int) - assert isinstance(gc_stats['garbage_count'], int) - assert gc_stats['objects_collected'] >= 0 - assert gc_stats['garbage_count'] >= 0 + assert isinstance(gc_stats["objects_collected"], int) + assert isinstance(gc_stats["garbage_count"], int) + assert gc_stats["objects_collected"] >= 0 + assert gc_stats["garbage_count"] >= 0 def test_memory_optimization_integration(self): """Test integration of all memory optimization features.""" @@ -723,20 +723,20 @@ def test_memory_optimization_integration(self): final_stats = get_memory_stats() # Verify optimization worked - assert final_stats['formatter_cache_size'] == 0 # Should be cleared - assert final_stats['directory_cache_size'] == 0 # Should be cleared - assert gc_result['objects_collected'] >= 0 + assert final_stats["formatter_cache_size"] == 0 # Should be cleared + assert final_stats["directory_cache_size"] == 0 # Should be cleared + assert gc_result["objects_collected"] >= 0 def test_memory_utils_module_constants(self): """Test module-level constants and their behavior.""" from pythonLogs.core import memory_utils # Verify module constants exist and have reasonable values - assert hasattr(memory_utils, '_formatter_cache') - assert hasattr(memory_utils, '_formatter_cache_lock') - assert hasattr(memory_utils, '_max_formatters') - assert hasattr(memory_utils, '_active_loggers') - assert hasattr(memory_utils, '_weak_ref_lock') + assert hasattr(memory_utils, "_formatter_cache") + assert hasattr(memory_utils, "_formatter_cache_lock") + assert hasattr(memory_utils, "_max_formatters") + assert hasattr(memory_utils, "_active_loggers") + assert hasattr(memory_utils, "_weak_ref_lock") # Verify default values assert memory_utils._max_formatters > 0 diff --git a/tests/performance/test_performance.py b/tests/performance/test_performance.py index 053ca75..a7bb469 100644 --- a/tests/performance/test_performance.py +++ b/tests/performance/test_performance.py @@ -26,7 +26,7 @@ from tests.core.test_log_utils import get_safe_timezone -@pytest.mark.skipif(os.getenv('CI') == 'true', reason="Performance tests unstable in CI") +@pytest.mark.skipif(os.getenv("CI") == "true", reason="Performance tests unstable in CI") class TestPerformance: """Performance tests for factory pattern and optimizations.""" @@ -97,7 +97,8 @@ def test_directory_permission_caching(self): start_time = time.time() for i in range(10): logger = SizeRotatingLog( - name=f"dir_test_{i+2}", directory=temp_dir # The Same directory should use cache + name=f"dir_test_{i + 2}", + directory=temp_dir, # The Same directory should use cache ) subsequent_calls_time = time.time() - start_time diff --git a/tests/performance/test_performance_windows.py b/tests/performance/test_performance_windows.py index 6f539db..118f944 100644 --- a/tests/performance/test_performance_windows.py +++ b/tests/performance/test_performance_windows.py @@ -24,7 +24,7 @@ from tests.core.test_log_utils import windows_safe_temp_directory -@pytest.mark.skipif(os.getenv('CI') == 'true', reason="Performance tests unstable in CI") +@pytest.mark.skipif(os.getenv("CI") == "true", reason="Performance tests unstable in CI") class TestPerformanceWindows: """Windows-specific performance tests for factory pattern and optimizations.""" @@ -45,7 +45,7 @@ def test_directory_permission_caching_windows(self): start_time = time.perf_counter() for i in range(10): logger = SizeRotatingLog( - name=f"dir_test_{i+2}_win", + name=f"dir_test_{i + 2}_win", directory=temp_dir, # The Same directory should use cache ) subsequent_calls_time = time.perf_counter() - start_time diff --git a/tests/performance/test_performance_zoneinfo.py b/tests/performance/test_performance_zoneinfo.py index 77490ba..2728bfd 100644 --- a/tests/performance/test_performance_zoneinfo.py +++ b/tests/performance/test_performance_zoneinfo.py @@ -18,7 +18,7 @@ from pythonLogs.core.factory import clear_logger_registry -@pytest.mark.skipif(os.getenv('CI') == 'true', reason="Performance tests unstable in CI") +@pytest.mark.skipif(os.getenv("CI") == "true", reason="Performance tests unstable in CI") class TestZoneinfoPerformance: """Performance tests for zoneinfo timezone operations.""" diff --git a/tests/thread_safety/test_automatic_features.py b/tests/thread_safety/test_automatic_features.py index 9986e75..cee3eb4 100644 --- a/tests/thread_safety/test_automatic_features.py +++ b/tests/thread_safety/test_automatic_features.py @@ -138,13 +138,13 @@ def test_automatic_features_verification(self): assert logger is not None # 2. Automatic Resource Cleanup: Context manager support - assert hasattr(basic_log, '__enter__') - assert hasattr(basic_log, '__exit__') - assert hasattr(basic_log, 'cleanup_logger') + assert hasattr(basic_log, "__enter__") + assert hasattr(basic_log, "__exit__") + assert hasattr(basic_log, "cleanup_logger") # 3. Automatic Thread Safety: Decorator applied - assert hasattr(basic_log.__class__, '_lock') - assert hasattr(basic_log.init, '_thread_safe_wrapped') + assert hasattr(basic_log.__class__, "_lock") + assert hasattr(basic_log.init, "_thread_safe_wrapped") # cleanup_logger is a static method, so it's not wrapped BasicLog.cleanup_logger(logger) diff --git a/tests/thread_safety/test_automatic_thread_safety.py b/tests/thread_safety/test_automatic_thread_safety.py index 6c60672..1b00455 100644 --- a/tests/thread_safety/test_automatic_thread_safety.py +++ b/tests/thread_safety/test_automatic_thread_safety.py @@ -124,8 +124,8 @@ def test_automatic_locking_verification(self): basic_log = BasicLog(name="test_lock_verification") # Verify the class has the automatic thread safety decorator applied - assert hasattr(basic_log.__class__, '_lock'), "Class should have automatic lock" - assert hasattr(basic_log.init, '_thread_safe_wrapped'), "Method should be wrapped for thread safety" + assert hasattr(basic_log.__class__, "_lock"), "Class should have automatic lock" + assert hasattr(basic_log.init, "_thread_safe_wrapped"), "Method should be wrapped for thread safety" # cleanup_logger is a static method, so it's not wrapped with thread safety # Test that methods can still be called normally diff --git a/tests/thread_safety/test_thread_safety.py b/tests/thread_safety/test_thread_safety.py index 898aba3..5b62466 100644 --- a/tests/thread_safety/test_thread_safety.py +++ b/tests/thread_safety/test_thread_safety.py @@ -63,7 +63,7 @@ def create_logger_worker(): def test_concurrent_registry_operations(self): """Test concurrent registry operations (create, shutdown, clear).""" num_threads = 20 - results = {'created': [], 'shutdown': [], 'errors': []} + results = {"created": [], "shutdown": [], "errors": []} def mixed_operations_worker(worker_id): """Worker that performs mixed registry operations.""" @@ -72,17 +72,17 @@ def mixed_operations_worker(worker_id): # Create logger logger = LoggerFactory.get_or_create_logger(LoggerType.BASIC, name=logger_name, level=LogLevel.DEBUG) - results['created'].append(logger_name) + results["created"].append(logger_name) # Small delay to increase chance of race conditions time.sleep(0.01) # Try to shut down logger if LoggerFactory.shutdown_logger(logger_name): - results['shutdown'].append(logger_name) + results["shutdown"].append(logger_name) except Exception as e: - results['errors'].append(str(e)) + results["errors"].append(str(e)) # Run concurrent operations with concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) as executor: @@ -91,10 +91,10 @@ def mixed_operations_worker(worker_id): future.result() # No errors should occur - assert len(results['errors']) == 0, f"Errors occurred: {results['errors']}" + assert len(results["errors"]) == 0, f"Errors occurred: {results['errors']}" # All created loggers should be accounted for - assert len(results['created']) == num_threads + assert len(results["created"]) == num_threads # Registry should be consistent registry = LoggerFactory.get_registered_loggers() @@ -193,7 +193,7 @@ def test_stress_test_factory_pattern(self): num_threads = 50 operations_per_thread = 10 logger_names = [f"stress_logger_{i}" for i in range(5)] # Shared logger names - results = {'success': 0, 'errors': []} + results = {"success": 0, "errors": []} results_lock = threading.Lock() def stress_worker(): @@ -219,11 +219,11 @@ def stress_worker(): time.sleep(0.001) with results_lock: - results['success'] += operations_per_thread + results["success"] += operations_per_thread except Exception as e: with results_lock: - results['errors'].append(str(e)) + results["errors"].append(str(e)) # Run stress test with concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) as executor: @@ -233,8 +233,8 @@ def stress_worker(): # Verify results expected_operations = num_threads * operations_per_thread - assert results['success'] == expected_operations, f"Expected {expected_operations}, got {results['success']}" - assert len(results['errors']) == 0, f"Stress test errors: {results['errors']}" + assert results["success"] == expected_operations, f"Expected {expected_operations}, got {results['success']}" + assert len(results["errors"]) == 0, f"Stress test errors: {results['errors']}" # Registry may have evicted some loggers due to memory limits or TTL # Just verify it has at least some loggers and doesn't exceed the total @@ -369,29 +369,29 @@ def _create_logger_and_messages(self, worker_id, temp_dir, results_lock, thread_ assert _message in _log_content with results_lock: - thread_results[worker_id] = {'messages': messages, 'log_content': _log_content} + thread_results[worker_id] = {"messages": messages, "log_content": _log_content} def _verify_thread_results(self, thread_results, num_threads): """Helper to verify all thread results are successful.""" for worker_id in range(num_threads): assert worker_id in thread_results assert ( - 'error' not in thread_results[worker_id] + "error" not in thread_results[worker_id] ), f"Thread {worker_id} failed: {thread_results[worker_id].get('error')}" - assert 'messages' in thread_results[worker_id] - assert len(thread_results[worker_id]['messages']) == 10 + assert "messages" in thread_results[worker_id] + assert len(thread_results[worker_id]["messages"]) == 10 def _check_worker_log_isolation(self, worker_id, log_content, thread_results, num_threads): """Check that a worker's log doesn't contain messages from other workers.""" for other_id in range(num_threads): if other_id != worker_id: - for message in thread_results[other_id]['messages']: + for message in thread_results[other_id]["messages"]: assert message not in log_content, f"Thread {worker_id} log contains message from thread {other_id}" def _verify_no_cross_contamination(self, thread_results, num_threads): """Helper to verify no cross-contamination between thread logs.""" for worker_id in range(num_threads): - log_content = thread_results[worker_id]['log_content'] + log_content = thread_results[worker_id]["log_content"] self._check_worker_log_isolation(worker_id, log_content, thread_results, num_threads) @pytest.mark.skipif( @@ -412,7 +412,7 @@ def independent_worker(worker_id): except Exception as e: with results_lock: - thread_results[worker_id] = {'error': str(e)} + thread_results[worker_id] = {"error": str(e)} # Run independent workers with concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) as executor: diff --git a/tests/thread_safety/test_thread_safety_module.py b/tests/thread_safety/test_thread_safety_module.py index 562c4fc..a429ddd 100644 --- a/tests/thread_safety/test_thread_safety_module.py +++ b/tests/thread_safety/test_thread_safety_module.py @@ -67,7 +67,7 @@ def increment(self): assert obj.counter == 1 # The lock should be accessible via the method's fallback mechanism - lock = getattr(obj, '_lock', None) or getattr(obj.__class__, '_lock', None) + lock = getattr(obj, "_lock", None) or getattr(obj.__class__, "_lock", None) assert lock is not None def test_thread_safe_decorator_preserves_metadata(self): @@ -86,8 +86,8 @@ def test_method(self, arg1, arg2=None): method = obj.test_method # Check that wrapper preserves original function name and docstring - assert method.__name__ == 'test_method' - assert 'Test method docstring' in method.__doc__ + assert method.__name__ == "test_method" + assert "Test method docstring" in method.__doc__ assert method(1, arg2=2) == "1-2" @@ -97,7 +97,7 @@ class TestAutoThreadSafeDecorator: def test_auto_thread_safe_specific_methods(self): """Test @auto_thread_safe with specific method list.""" - @auto_thread_safe(['increment']) + @auto_thread_safe(["increment"]) class TestClass: def __init__(self): self.counter = 0 @@ -113,9 +113,9 @@ def unsafe_increment(self): obj = TestClass() # Check that the specified method is wrapped - assert hasattr(obj.increment, '_thread_safe_wrapped') + assert hasattr(obj.increment, "_thread_safe_wrapped") # Check that non-specified method is not wrapped - assert not hasattr(obj.unsafe_increment, '_thread_safe_wrapped') + assert not hasattr(obj.unsafe_increment, "_thread_safe_wrapped") # Test thread safety of wrapped method threads = [] @@ -152,15 +152,15 @@ def _private_method(self): obj = TestClass() # Public methods should be wrapped - assert hasattr(obj.increment, '_thread_safe_wrapped') - assert hasattr(obj.decrement, '_thread_safe_wrapped') + assert hasattr(obj.increment, "_thread_safe_wrapped") + assert hasattr(obj.decrement, "_thread_safe_wrapped") # Private method should not be wrapped - assert not hasattr(obj._private_method, '_thread_safe_wrapped') + assert not hasattr(obj._private_method, "_thread_safe_wrapped") def test_auto_thread_safe_no_double_wrapping(self): """Test that methods are not wrapped multiple times.""" - @auto_thread_safe(['test_method']) + @auto_thread_safe(["test_method"]) class TestClass: def test_method(self): return "test" @@ -168,7 +168,7 @@ def test_method(self): obj = TestClass() # Apply decorator again (should not double-wrap) - wrapped_test_class = auto_thread_safe(['test_method'])(TestClass) + wrapped_test_class = auto_thread_safe(["test_method"])(TestClass) obj2 = wrapped_test_class() # Should still work and not be double-wrapped @@ -182,7 +182,7 @@ def test_thread_safe_meta_basic(self): """Test basic ThreadSafeMeta functionality.""" class TestClass(metaclass=ThreadSafeMeta): - _thread_safe_methods = ['increment'] + _thread_safe_methods = ["increment"] def __init__(self): self.counter = 0 @@ -198,7 +198,7 @@ def unsafe_method(self): obj = TestClass() # Should have class-level lock - assert hasattr(obj.__class__, '_lock') + assert hasattr(obj.__class__, "_lock") # Test that increment method works obj.increment() assert obj.counter == 1 @@ -219,7 +219,7 @@ def _private_method(self): obj = TestClass() # Should have class-level lock - assert hasattr(obj.__class__, '_lock') + assert hasattr(obj.__class__, "_lock") # Test that methods work assert obj.public_method() == "public" assert obj._private_method() == "private" @@ -244,9 +244,9 @@ def increment(self): obj = TestClass() # Should have instance lock - assert hasattr(obj, '_lock') + assert hasattr(obj, "_lock") # Public method should be wrapped - assert hasattr(obj.increment, '_thread_safe_wrapped') + assert hasattr(obj.increment, "_thread_safe_wrapped") # Test thread safety threads = [] @@ -282,8 +282,8 @@ def derived_method(self): obj = DerivedClass() # Both base and derived methods should be thread-safe - assert hasattr(obj.base_method, '_thread_safe_wrapped') - assert hasattr(obj.derived_method, '_thread_safe_wrapped') + assert hasattr(obj.base_method, "_thread_safe_wrapped") + assert hasattr(obj.derived_method, "_thread_safe_wrapped") class TestSynchronizedMethodDecorator: @@ -364,7 +364,7 @@ class TestEdgeCases: def test_thread_safe_with_static_methods(self): """Test thread safety with static methods.""" - @auto_thread_safe(['regular_method']) + @auto_thread_safe(["regular_method"]) class TestClass: counter = 0 @@ -378,14 +378,14 @@ def static_method(): obj = TestClass() # Regular method should be wrapped - assert hasattr(obj.regular_method, '_thread_safe_wrapped') + assert hasattr(obj.regular_method, "_thread_safe_wrapped") # Static method should not be affected assert TestClass.static_method() == "static" def test_thread_safe_with_class_methods(self): """Test thread safety with class methods.""" - @auto_thread_safe(['regular_method']) + @auto_thread_safe(["regular_method"]) class TestClass: counter = 0 @@ -399,14 +399,14 @@ def class_method(cls): obj = TestClass() # Regular method should be wrapped - assert hasattr(obj.regular_method, '_thread_safe_wrapped') + assert hasattr(obj.regular_method, "_thread_safe_wrapped") # Class method should work normally assert TestClass.class_method() == "class" def test_thread_safe_with_properties(self): """Test thread safety with properties.""" - @auto_thread_safe(['set_value']) + @auto_thread_safe(["set_value"]) class TestClass: def __init__(self): self._value = 0 @@ -437,8 +437,8 @@ def increment(self): self.counter += 1 # Apply auto_thread_safe multiple times - wrapped_test_class = auto_thread_safe(['increment'])(TestClass) - wrapped_test_class = auto_thread_safe(['increment'])(wrapped_test_class) + wrapped_test_class = auto_thread_safe(["increment"])(TestClass) + wrapped_test_class = auto_thread_safe(["increment"])(wrapped_test_class) obj = wrapped_test_class() obj.increment() diff --git a/tests/thread_safety/test_thread_safety_patterns.py b/tests/thread_safety/test_thread_safety_patterns.py index 9e3083e..6d20120 100644 --- a/tests/thread_safety/test_thread_safety_patterns.py +++ b/tests/thread_safety/test_thread_safety_patterns.py @@ -97,7 +97,7 @@ def write(self, key, value): def get_stats(self): with self._lock: - return {'reads': self.read_count, 'writes': self.write_count, 'data_size': len(self.data)} + return {"reads": self.read_count, "writes": self.write_count, "data_size": len(self.data)} store = ThreadSafeDataStore() @@ -132,9 +132,9 @@ def reader(keys): future.result() stats = store.get_stats() - assert stats['writes'] == 50 - assert stats['data_size'] == 50 - assert stats['reads'] > 0 # Some reads should have occurred + assert stats["writes"] == 50 + assert stats["data_size"] == 50 + assert stats["reads"] > 0 # Some reads should have occurred def test_singleton_pattern_thread_safety(self): """Test thread-safe singleton pattern.""" @@ -217,9 +217,9 @@ def return_resource(self, resource): def stats(self): with self._lock: return { - 'available': len(self.available), - 'in_use': len(self.in_use), - 'total': len(self.available) + len(self.in_use), + "available": len(self.available), + "in_use": len(self.in_use), + "total": len(self.available) + len(self.in_use), } # Create a simple resource (just a counter) @@ -248,9 +248,9 @@ def worker(worker_id): future.result() stats = pool.stats() - assert stats['total'] == 3 # Pool size maintained - assert stats['available'] == 3 # All resources returned - assert stats['in_use'] == 0 # No resources stuck + assert stats["total"] == 3 # Pool size maintained + assert stats["available"] == 3 # All resources returned + assert stats["in_use"] == 0 # No resources stuck assert len(completed_tasks) == 8 # All workers completed def test_cache_with_expiry_thread_safety(self): @@ -363,9 +363,9 @@ def publish(self, event_type, data): def get_stats(self): with self._lock: return { - 'subscriber_count': sum(len(subs) for subs in self.subscribers.values()), - 'event_types': len(self.subscribers), - 'events_published': dict(self.event_count), + "subscriber_count": sum(len(subs) for subs in self.subscribers.values()), + "event_types": len(self.subscribers), + "events_published": dict(self.event_count), } event_bus = ThreadSafeEventBus() @@ -418,9 +418,9 @@ def subscriber(event_type): assert len(type_a_events) > 0 # type_A had 2 subscribers assert len(type_b_events) > 0 # type_B had 1 subscriber - assert stats['events_published']['type_A'] == 10 - assert stats['events_published']['type_B'] == 5 - assert stats['events_published']['type_C'] == 3 + assert stats["events_published"]["type_A"] == 10 + assert stats["events_published"]["type_B"] == 5 + assert stats["events_published"]["type_C"] == 3 def test_weak_reference_cleanup_thread_safety(self): """Test thread safety with weak references and cleanup.""" diff --git a/tests/timezone/test_timezone_migration.py b/tests/timezone/test_timezone_migration.py index 9ca8791..529f034 100644 --- a/tests/timezone/test_timezone_migration.py +++ b/tests/timezone/test_timezone_migration.py @@ -132,12 +132,12 @@ def test_timezone_offset_calculation(self): # UTC should return +0000, but may fall back to localtime on Windows assert isinstance(utc_offset, str) assert len(utc_offset) == 5 - assert utc_offset[0] in ['+', '-'] + assert utc_offset[0] in ["+", "-"] # Test localtime local_offset = get_timezone_offset("localtime") assert len(local_offset) == 5 # Format: ±HHMM - assert local_offset[0] in ['+', '-'] + assert local_offset[0] in ["+", "-"] def test_timezone_function_caching(self): """Test that timezone functions are properly cached.""" diff --git a/tests/timezone/test_zoneinfo_fallbacks.py b/tests/timezone/test_zoneinfo_fallbacks.py index 80151b8..b70c945 100644 --- a/tests/timezone/test_zoneinfo_fallbacks.py +++ b/tests/timezone/test_zoneinfo_fallbacks.py @@ -33,7 +33,9 @@ def test_timezone_error_handling(self): # With the new fallback system, invalid timezones should gracefully fall back # to localtime instead of raising exceptions for better robustness logger = BasicLog( - name="error_test", timezone="NonExistent/Timezone", level=LogLevel.INFO # Should fall back to localtime + name="error_test", + timezone="NonExistent/Timezone", + level=LogLevel.INFO, # Should fall back to localtime ) # Logger should be created successfully with fallback assert logger.name == "error_test" @@ -48,13 +50,13 @@ def test_timezone_offset_edge_cases(self): # UTC should return +0000, but may fall back to localtime on Windows assert isinstance(utc_offset, str) assert len(utc_offset) == 5 - assert utc_offset[0] in ['+', '-'] + assert utc_offset[0] in ["+", "-"] # Test localtime (should work on any system) local_offset = get_timezone_offset("localtime") assert isinstance(local_offset, str) assert len(local_offset) == 5 - assert local_offset[0] in ['+', '-'] + assert local_offset[0] in ["+", "-"] # Test case insensitivity for localtime local_offset_upper = get_timezone_offset("LOCALTIME") @@ -67,7 +69,7 @@ def test_stderr_timezone_fallback(self): from pythonLogs.core.log_utils import write_stderr # Mock environment variable - with patch.dict(os.environ, {'LOG_TIMEZONE': 'UTC'}): + with patch.dict(os.environ, {"LOG_TIMEZONE": "UTC"}): stderr_capture = io.StringIO() with redirect_stderr(stderr_capture): write_stderr("Test message") @@ -151,7 +153,7 @@ def test_environment_variable_timezone_handling(self): """Test timezone handling through environment variables.""" # Test with environment variable - with patch.dict(os.environ, {'LOG_TIMEZONE': 'Europe/Paris'}): + with patch.dict(os.environ, {"LOG_TIMEZONE": "Europe/Paris"}): # Environment variable should be used for stderr from pythonLogs.core.log_utils import get_stderr_timezone @@ -235,11 +237,11 @@ def test_timezone_validation_edge_cases(self): # For localtime, just check format assert isinstance(result, str) assert len(result) == 5 - assert result[0] in ['+', '-'] + assert result[0] in ["+", "-"] # Test that invalid timezone names now fall back gracefully to localtime result = get_timezone_offset("invalid_timezone") # Should fall back to localtime format assert isinstance(result, str) assert len(result) == 5 - assert result[0] in ['+', '-'] + assert result[0] in ["+", "-"] From dfa2529c2828705a58ff53fb70f7557d6d5a7cf9 Mon Sep 17 00:00:00 2001 From: ddc Date: Thu, 5 Feb 2026 17:21:13 -0300 Subject: [PATCH 7/9] V6.0.2 --- .github/workflows/workflow.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 555f2a1..d7d84aa 100755 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -40,7 +40,7 @@ jobs: run: uv python install ${{ matrix.python-version }} - name: Install dependencies - run: uv sync --group dev + run: uv sync --all-extras --group dev shell: bash - name: Run tests with coverage @@ -48,7 +48,7 @@ jobs: with: timeout_minutes: 2 max_attempts: 3 - command: uv run pytest + command: uv run --no-sync pytest shell: bash - name: Upload coverage to Codecov From 7cb507b460b253ec09c04ec292773a165a11016b Mon Sep 17 00:00:00 2001 From: ddc Date: Fri, 6 Feb 2026 13:25:26 -0300 Subject: [PATCH 8/9] V6.0.2 --- .github/workflows/workflow.yml | 2 +- .pre-commit-config.yaml | 5 +++++ pyproject.toml | 10 ++++------ uv.lock | 4 ---- 4 files changed, 10 insertions(+), 11 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index d7d84aa..87fcb6b 100755 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -40,7 +40,7 @@ jobs: run: uv python install ${{ matrix.python-version }} - name: Install dependencies - run: uv sync --all-extras --group dev + run: uv sync --locked --all-extras --dev shell: bash - name: Run tests with coverage diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..4b84c55 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,5 @@ +repos: + - repo: https://github.com/astral-sh/uv-pre-commit + rev: 0.10.0 + hooks: + - id: uv-lock diff --git a/pyproject.toml b/pyproject.toml index 5b8112a..88465df 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,9 +52,7 @@ dependencies = [ [dependency-groups] dev = [ "psutil>=7.2.2", - "pytest>=9.0.2", "pytest-cov>=7.0.0", - "coverage>=7.13.3", "black>=26.1.0", "poethepoet>=0.40.0", "ruff>=0.15.0", @@ -62,10 +60,10 @@ dev = [ [tool.poe.tasks] linter.shell = "uv run ruff check --fix . && uv run black ." -profile = "uv run python -m cProfile -o cprofile_unit.prof -m pytest --no-cov" -test = "uv run pytest" -updatedev.sequence = ["linter", {shell = "uv lock && uv sync --all-extras --group dev"}] -build.sequence = ["updatedev", {shell = "uv build --wheel"}] +profile.sequence = ["linter", {shell = "uv run python -m cProfile -o cprofile_unit.prof -m pytest --no-cov"}] +test.sequence = ["linter", {shell = "uv run pytest"}] +updatedev.sequence = ["linter", {shell = "uv lock --upgrade && uv sync --all-extras --group dev"}] +build.sequence = ["updatedev", "test", {shell = "uv build --wheel"}] [tool.pytest.ini_options] addopts = "-v --cov --cov-report=term --cov-report=xml --junitxml=junit.xml" diff --git a/uv.lock b/uv.lock index c6cb5e5..555ff99 100644 --- a/uv.lock +++ b/uv.lock @@ -450,10 +450,8 @@ dependencies = [ [package.dev-dependencies] dev = [ { name = "black" }, - { name = "coverage" }, { name = "poethepoet" }, { name = "psutil" }, - { name = "pytest" }, { name = "pytest-cov" }, { name = "ruff" }, ] @@ -464,10 +462,8 @@ requires-dist = [{ name = "pydantic-settings", specifier = ">=2.11.0" }] [package.metadata.requires-dev] dev = [ { name = "black", specifier = ">=26.1.0" }, - { name = "coverage", specifier = ">=7.13.3" }, { name = "poethepoet", specifier = ">=0.40.0" }, { name = "psutil", specifier = ">=7.2.2" }, - { name = "pytest", specifier = ">=9.0.2" }, { name = "pytest-cov", specifier = ">=7.0.0" }, { name = "ruff", specifier = ">=0.15.0" }, ] From adc675447afca519f87f0ac1d41b54ad2b73b750 Mon Sep 17 00:00:00 2001 From: ddc Date: Fri, 6 Feb 2026 15:32:55 -0300 Subject: [PATCH 9/9] V6.0.2 --- .github/workflows/workflow.yml | 12 ++- README.md | 32 ++++-- tests/README.md | 180 --------------------------------- tests/smoke_test.py | 10 ++ 4 files changed, 45 insertions(+), 189 deletions(-) delete mode 100644 tests/README.md create mode 100644 tests/smoke_test.py diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 87fcb6b..cf1c821 100755 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -1,8 +1,8 @@ name: CI/CD Pipeline 'on': + pull_request: push: - branches: ['**'] tags: ['v*'] env: @@ -35,6 +35,8 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v7 + with: + enable-cache: true - name: Set up Python ${{ matrix.python-version }} run: uv python install ${{ matrix.python-version }} @@ -71,6 +73,8 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v7 + with: + enable-cache: true - name: Set up Python ${{ env.LATEST_PYTHON_VERSION }} run: uv python install ${{ env.LATEST_PYTHON_VERSION }} @@ -78,6 +82,12 @@ jobs: - name: Build package run: uv build + - name: Smoke test (wheel) + run: uv run --isolated --no-project --with dist/*.whl tests/smoke_test.py + + - name: Smoke test (sdist) + run: uv run --isolated --no-project --with dist/*.tar.gz tests/smoke_test.py + - name: Upload artifacts uses: actions/upload-artifact@v6 with: diff --git a/README.md b/README.md index 24b8692..897b570 100755 --- a/README.md +++ b/README.md @@ -41,7 +41,9 @@ - [Settings Cache Management](#settings-cache-management) - [Flexible Configuration Options](#flexible-configuration-options) - [Development](#development) - - [Create DEV Environment, Running Tests and Building Wheel](#create-dev-environment-running-tests-and-building-wheel) + - [Create DEV Environment and Running Tests](#create-dev-environment-and-running-tests) + - [Update DEV Environment Packages](#update-dev-environment-packages) + - [Building Wheel](#building-wheel) - [Optionals](#optionals) - [License](#license) - [Support](#support) @@ -349,20 +351,34 @@ RotateWhen.MONDAY # "W0" # Development -Must have [UV](https://uv.run/docs/getting-started/installation), -[Black](https://black.readthedocs.io/en/stable/getting_started.html), -[Ruff](https://docs.astral.sh/ruff/installation/), and -[Poe the Poet](https://poethepoet.naber.dev/installation) installed. +Must have [UV](https://uv.run/docs/getting-started/installation) installed. -## Create DEV Environment, Running Tests and Building Wheel +## Create DEV Environment and Running Tests + +> **Note:** All poe tasks automatically run ruff linter along with Black formatting ```shell -uv sync --all-extras -poe linter +uv sync --all-extras --all-groups poe test +``` + + +## Update DEV Environment Packages +This will update all packages dependencies + +```shell +poe updatedev +``` + + +## Building Wheel +This will update all packages, run linter, both unit and integration tests and finally build the wheel + +```shell poe build ``` + ## Optionals ### Create a cprofile.prof file from unit tests diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index babede3..0000000 --- a/tests/README.md +++ /dev/null @@ -1,180 +0,0 @@ -# Test Suite Documentation - -This directory contains comprehensive tests for the pythonLogs library, organized into logical categories for better maintainability and navigation. - -## Test Directory Structure - -``` -tests/ -├── core/ # Core functionality tests -├── context_management/ # Context managers & resource management -├── logger_types/ # Specific logger type tests -├── factory/ # Factory pattern tests -├── performance/ # Performance & memory optimization tests -├── thread_safety/ # Thread safety & concurrency tests -└── timezone/ # Timezone functionality tests -``` - -## Test Files Overview - -### Core Functionality Tests (`tests/core/`) -- **`test_basic_log.py`** - Comprehensive BasicLog functionality testing - - Tests BasicLog initialization, context managers, thread safety - - Validates cleanup methods and multiple instance handling - - **10 test cases** covering all BasicLog features - -- **`test_log_utils.py`** - Tests for utility functions - - Tests helper functions in `log_utils.py` - - Includes file operations, timezone handling, and validation - - Multiple test cases for various utilities - -### Context Manager & Resource Management Tests (`tests/context_management/`) -- **`test_context_managers.py`** - Context manager functionality for all logger types - - Tests automatic resource cleanup for BasicLog, SizeRotatingLog, TimedRotatingLog - - Validates exception safety and proper handler cleanup - - Tests nested context managers and multiple file handlers - - **10 test cases** including the new `shutdown_logger` test - -- **`test_resource_management.py`** - Resource lifecycle management - - Test factory registry cleanup and memory management - - Validates handler cleanup and resource disposal - - Tests concurrent access safety and performance - - **9 test cases** for robust resource management - -### Logger Type Tests (`tests/logger_types/`) -- **`test_size_rotating.py`** - Size-based rotating logger tests - - Tests file rotation, compression, and cleanup - - Context manager functionality and resource management - - Multiple file handling and stream output - - Comprehensive size rotation scenarios - -- **`test_timed_rotating.py`** - Time-based rotating logger tests - - Tests time-based rotation (hourly, daily, midnight, weekdays) - - Context manager functionality and resource management - - Timezone handling and rotation scheduling - - Comprehensive time rotation scenarios - -### Factory Pattern Tests (`tests/factory/`) -- **`test_factory.py`** - Core factory pattern functionality - - Tests `LoggerFactory` class and all factory methods - - Validates logger creation, registry caching, and performance - - Tests error handling and type validation - - **Multiple test cases** covering all factory features - -- **`test_enums.py`** - Enum usage with factory pattern - - Tests `LogLevel`, `RotateWhen`, and `LoggerType` enums - - Validates enum-to-string conversion and type safety - - Tests backward compatibility with string values - - **10 test cases** covering all enum scenarios - -- **`test_factory_examples.py`** - Integration and practical examples - - Real-world usage scenarios and production-like setups - - Multi-logger configurations and file-based logging - - Registry usage patterns and error scenarios - - **Multiple test cases** demonstrating practical usage - -- **`test_string_levels.py`** - String-based level configuration - - Tests case-insensitive string level handling - - Validates string to enum conversion - - Tests all logger types with string levels - - Comprehensive string level compatibility - -### Performance & Memory Tests (`tests/performance/`) -- **`test_performance.py`** - Performance and optimization tests - - Validates caching improvements and performance gains - - Tests settings caching, registry performance, and memory usage - - Stress testing and large-scale logger creation - - Performance benchmarking for optimization features - -- **`test_memory_optimization.py`** - Memory management and optimization - - Test memory usage patterns and cleanup efficiency - - Validates formatter caching and directory caching - - Tests garbage collection and memory leak prevention - - Memory optimization feature validation - -- **`test_performance_zoneinfo.py`** - Performance tests for timezone operations - - Benchmarks timezone function caching and optimization - - Tests performance under concurrent access and bulk operations - - Validates memory efficiency of timezone caching - - Timezone performance optimization validation - -### Thread Safety & Concurrency Tests (`tests/thread_safety/`) -- **`test_thread_safety.py`** - Concurrency and thread safety - - Tests concurrent logger creation and registry access - - Validates thread-safe operations across all components - - Tests concurrent context manager cleanup - - Stress testing for multithreaded environments - -- **`test_automatic_thread_safety.py`** - Automatic thread safety implementation - - Tests automatic thread-safety decorators applied to logger classes - - Validates @auto_thread_safe decorator functionality - - Tests BasicLog, SizeRotatingLog, and TimedRotatingLog with automatic locking - - **4 test cases** covering automatic thread safety features - -- **`test_thread_safety_module.py`** - Comprehensive thread safety module tests - - Test all thread safety decorators (@thread_safe, @auto_thread_safe) - - Tests ThreadSafeMeta metaclass and AutoThreadSafe base class - - Tests ThreadSafeContext context manager and edge cases - - **19 test cases** covering all thread safety mechanisms - -- **`test_thread_safety_patterns.py`** - Advanced thread safety patterns - - Tests real-world concurrent patterns (producer-consumer, singleton, etc.) - - Tests resource pool, event bus, and cache patterns with thread safety - - Tests weak reference cleanup in multithreaded environments - - **8 test cases** covering complex thread safety scenarios - -- **`test_automatic_features.py`** - Integration of all automatic features - - Test memory optimization, resource cleanup, and thread safety together - - Validates all three automatic features work seamlessly - - Tests stress scenarios with multiple logger types concurrently - - **6 test cases** ensuring all automatic features integrate properly - -### Timezone & Migration Tests (`tests/timezone/`) -- **`test_timezone_migration.py`** - Timezone functionality with zoneinfo - - Tests migration from pytz to Python's built-in zoneinfo module - - Validates UTC, localtime, and named timezone support - - Tests timezone integration with all logger types and factory pattern - - **Multiple test cases** covering comprehensive timezone scenarios - -- **`test_zoneinfo_fallbacks.py`** - Timezone fallback mechanisms and edge cases - - Tests fallback behavior for systems without complete timezone data - - Validates error handling and edge cases for timezone operations - - Tests concurrent access and memory efficiency - - **Multiple test cases** for robust timezone handling - - - -## Running Tests - -### Run All Tests and Create a Coverage Report -```bash -poetry run poe test -``` - -### Run Specific Test Categories -```bash -# Core functionality tests -poetry run pytest tests/core/ -v - -# Context managers and resource management -poetry run pytest tests/context_management/ -v - -# Logger type tests (size rotating, timed rotating) -poetry run pytest tests/logger_types/ -v - -# Factory pattern tests -poetry run pytest tests/factory/ -v - -# Performance and memory optimization tests -poetry run pytest tests/performance/ -v - -# Thread safety and concurrency tests -poetry run pytest tests/thread_safety/ -v - -# Timezone functionality tests -poetry run pytest tests/timezone/ -v - -# Run specific directories together -poetry run pytest tests/core/ tests/logger_types/ -v # Core + Logger types -poetry run pytest tests/performance/ tests/thread_safety/ -v # Performance + Concurrency -``` diff --git a/tests/smoke_test.py b/tests/smoke_test.py new file mode 100644 index 0000000..01ab688 --- /dev/null +++ b/tests/smoke_test.py @@ -0,0 +1,10 @@ +"""Smoke test to verify the built package works correctly.""" + +from pythonLogs import BasicLog, SizeRotatingLog, TimedRotatingLog, __version__ + +assert __version__, "Version should not be empty" +assert BasicLog, "BasicLog should be importable" +assert TimedRotatingLog, "TimedRotatingLog should be importable" +assert SizeRotatingLog, "SizeRotatingLog should be importable" + +print(f"pythonLogs {__version__} OK")