From 485447ceafe73d9831c5ea7882accb21188397f5 Mon Sep 17 00:00:00 2001 From: Pablo Aguilar Date: Fri, 28 Aug 2026 23:23:50 -0300 Subject: [PATCH 01/12] chore: add `pytest` with `cov` and `randomly` as dev dependencies # Conflicts: # poetry.lock --- poetry.lock | 226 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 34 +++++++- 2 files changed, 253 insertions(+), 7 deletions(-) diff --git a/poetry.lock b/poetry.lock index a5520fa..848dccd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,17 @@ -# This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] [[package]] name = "coverage" @@ -6,7 +19,7 @@ version = "7.15.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.10" -groups = ["main", "test"] +groups = ["dev"] files = [ {file = "coverage-7.15.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d0be6daac4cce6b8c8dc65886bae1b082ddbca4da8e5cbb5e15166acf253e264"}, {file = "coverage-7.15.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b24e078eabcd6a9caa8b0713f9bc1eeb310bcc960a29d45a3b4fcd4b16d5b11d"}, @@ -131,6 +144,9 @@ files = [ {file = "coverage-7.15.4.tar.gz", hash = "sha256:0548198fff07ccf4faf469520bce1c2eceb1ce3e62891921138dec10907f9d00"}, ] +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + [package.extras] toml = ["tomli ; python_full_version <= \"3.11.0a6\""] @@ -145,6 +161,25 @@ files = [ {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, ] +[[package]] +name = "exceptiongroup" +version = "1.3.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version == \"3.10\"" +files = [ + {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, + {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "holidays" version = "0.103" @@ -160,6 +195,18 @@ files = [ [package.dependencies] python-dateutil = ">=2.9.0.post0,<3" +[[package]] +name = "iniconfig" +version = "2.3.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, + {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, +] + [[package]] name = "num2words" version = "0.5.14" @@ -175,6 +222,108 @@ files = [ [package.dependencies] docopt = ">=0.6.2" +[[package]] +name = "packaging" +version = "26.3" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c"}, + {file = "packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79"}, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + +[[package]] +name = "pygments" +version = "2.21.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9"}, + {file = "pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pytest" +version = "9.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c"}, + {file = "pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} +iniconfig = ">=1.0.1" +packaging = ">=22" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "7.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678"}, + {file = "pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2"}, +] + +[package.dependencies] +coverage = {version = ">=7.10.6", extras = ["toml"]} +pluggy = ">=1.2" +pytest = ">=7" + +[package.extras] +testing = ["process-tests", "pytest-xdist", "virtualenv"] + +[[package]] +name = "pytest-randomly" +version = "4.1.0" +description = "Pytest plugin to randomly order tests and control random.seed." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "pytest_randomly-4.1.0-py3-none-any.whl", hash = "sha256:f55e89e53367b090c0c053697d7f9d77595543d0e0516c93978b50c0f6b252f9"}, + {file = "pytest_randomly-4.1.0.tar.gz", hash = "sha256:47f1d9746c3bc3efabd53ae1ebfb8bb385cf3d4df4b505b6d58d9c97a3dfe70f"}, +] + +[package.dependencies] +pytest = "*" + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -230,7 +379,78 @@ files = [ {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] +[[package]] +name = "tomli" +version = "2.4.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +markers = "python_full_version <= \"3.11.0a6\"" +files = [ + {file = "tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30"}, + {file = "tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a"}, + {file = "tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076"}, + {file = "tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9"}, + {file = "tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c"}, + {file = "tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc"}, + {file = "tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049"}, + {file = "tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e"}, + {file = "tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece"}, + {file = "tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a"}, + {file = "tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085"}, + {file = "tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9"}, + {file = "tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5"}, + {file = "tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585"}, + {file = "tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1"}, + {file = "tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917"}, + {file = "tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9"}, + {file = "tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257"}, + {file = "tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54"}, + {file = "tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a"}, + {file = "tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897"}, + {file = "tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f"}, + {file = "tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d"}, + {file = "tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5"}, + {file = "tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd"}, + {file = "tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36"}, + {file = "tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd"}, + {file = "tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf"}, + {file = "tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac"}, + {file = "tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662"}, + {file = "tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853"}, + {file = "tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15"}, + {file = "tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba"}, + {file = "tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6"}, + {file = "tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7"}, + {file = "tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232"}, + {file = "tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4"}, + {file = "tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c"}, + {file = "tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d"}, + {file = "tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41"}, + {file = "tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c"}, + {file = "tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f"}, + {file = "tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8"}, + {file = "tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26"}, + {file = "tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396"}, + {file = "tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe"}, + {file = "tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f"}, +] + +[[package]] +name = "typing-extensions" +version = "4.16.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version == \"3.10\"" +files = [ + {file = "typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8"}, + {file = "typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5"}, +] + [metadata] lock-version = "2.1" python-versions = "^3.10" -content-hash = "117f4082f37ca9944f53036d17af16b97b67e9e91d69ae6f5bf3da4a091cb263" +content-hash = "6705e6c3675c575cb3985d77f143794f8b916771a115981c73e7f6103dcec92e" diff --git a/pyproject.toml b/pyproject.toml index 997ddab..34aaa09 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,13 +28,12 @@ classifiers = [ python = "^3.10" holidays = ">=0.58,<0.104" num2words = "0.5.14" -coverage = "^7.2.7" - -[tool.poetry.group.test.dependencies] -coverage = "^7.2.7" [tool.poetry.group.dev.dependencies] ruff = "0.15.17" +pytest = "^9.1.1" +pytest-cov = "^7.1.0" +pytest-randomly = "^4.1.0" [tool.ruff] line-length = 80 @@ -43,6 +42,33 @@ lint.extend-select = ["I"] [tool.ruff.lint.per-file-ignores] "__init__.py" = ["F401"] +[tool.pytest] +minversion = "9.1" +norecursedirs = [ + "*.egg", + ".eggs", + "dist", + "build", + ".git", + "__pycache__", +] +addopts = [ + "--strict-markers", + "--strict-config", + "--doctest-modules", + "--doctest-glob=README*.md", + # pytest-cov + "--cov=brutils", + "--cov-report=term-missing:skip-covered", + "--cov-report=html", + "--cov-report=xml", + "--cov-branch", +] +testpaths = [ + "brutils", + "tests", +] + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" From a700a859257d0de4394fc27e4eb1b15326c8fbfc Mon Sep 17 00:00:00 2001 From: Pablo Aguilar Date: Fri, 28 Aug 2026 23:24:31 -0300 Subject: [PATCH 02/12] fix: fix docstring example codes --- brutils/cep.py | 49 ++++++++++++++++++------------------ brutils/cnpj.py | 29 +++++++++++---------- brutils/cpf.py | 12 ++++----- brutils/currency.py | 1 - brutils/ibge/municipality.py | 10 +++----- brutils/legal_nature.py | 1 - brutils/legal_process.py | 8 +++--- brutils/license_plate.py | 34 +++++++++++-------------- brutils/passport.py | 21 ++++++++-------- brutils/phone.py | 12 ++++----- brutils/pis.py | 18 ++++++------- 11 files changed, 92 insertions(+), 103 deletions(-) diff --git a/brutils/cep.py b/brutils/cep.py index 46ef975..40ba177 100644 --- a/brutils/cep.py +++ b/brutils/cep.py @@ -26,9 +26,9 @@ def remove_symbols(dirty: str) -> str: Example: >>> remove_symbols("123-45.678.9") - "123456789" + '123456789' >>> remove_symbols("abc.xyz") - "abcxyz" + 'abcxyz' """ return "".join(filter(lambda char: char not in ".-", dirty)) @@ -50,9 +50,9 @@ def format_cep(cep: str) -> str | None: Example: >>> format_cep("12345678") - "12345-678" + '12345-678' + >>> format_cep("12345") - None """ return f"{cep[:5]}-{cep[5:8]}" if is_valid(cep) else None @@ -100,8 +100,8 @@ def generate() -> str: str: A randomly generated 8-digit number. Example: - >>> generate() - "12345678" + >>> generate() # doctest: +SKIP + '12345678' """ generated_number = "" @@ -131,7 +131,7 @@ def get_address_from_cep( Address | None: An Address object (TypedDict) containing the address information if the CEP is found, None otherwise. Example: - >>> get_address_from_cep("12345678") + >>> get_address_from_cep("12345678") # doctest: +SKIP { "cep": "12345-678", "logradouro": "Rua Example", @@ -146,13 +146,14 @@ def get_address_from_cep( } >>> get_address_from_cep("abcdefg") - None >>> get_address_from_cep("abcdefg", True) - InvalidCEP: CEP 'abcdefg' is invalid. + Traceback (most recent call last): + brutils.exceptions.cep.InvalidCEP: CEP 'abcdefg' is invalid. >>> get_address_from_cep("00000000", True) - CEPNotFound: 00000000 + Traceback (most recent call last): + brutils.exceptions.cep.CEPNotFound: 00000000 """ base_api_url = "https://viacep.com.br/ws/{}/json/" @@ -202,30 +203,30 @@ def get_cep_information_from_address( list[Address] | None: A list of Address objects (TypedDict) containing the address information if the address is found, None otherwise. Example: - >>> get_cep_information_from_address("EX", "Example", "Rua Example") + >>> get_cep_information_from_address("SP", "São Paulo", "Tua Thomaz Gonzaga") # doctest: +SKIP [ { - "cep": "12345-678", - "logradouro": "Rua Example", - "complemento": "", - "bairro": "Example", - "localidade": "Example", - "uf": "EX", - "ibge": "1234567", - "gia": "1234", - "ddd": "12", - "siafi": "1234" + 'cep': '01506-020', + 'logradouro': 'Rua Thomaz Gonzaga', + 'complemento': '', + 'bairro': 'Liberdade', + 'localidade': 'São Paulo', + 'uf': 'SP', + 'ibge': '3550308', + 'gia': '1004', + 'ddd': '11', + 'siafi': '7107' } ] >>> get_cep_information_from_address("A", "Example", "Rua Example") - None >>> get_cep_information_from_address("XX", "Example", "Example", True) + Traceback (most recent call last): ValueError: Invalid UF: XX - >>> get_cep_information_from_address("SP", "Example", "Example", True) - CEPNotFound: SP - Example - Example + >>> get_cep_information_from_address("SP", "Example", "Example", True) # doctest: +SKIP + brutils.exceptions.cep.CEPNotFound: SP - Example - Example """ if federal_unit in UF.values: federal_unit = UF(federal_unit).name diff --git a/brutils/cnpj.py b/brutils/cnpj.py index f0b27cd..36e649d 100644 --- a/brutils/cnpj.py +++ b/brutils/cnpj.py @@ -22,9 +22,9 @@ def sieve(dirty: str) -> str: Example: >>> sieve("12.345/6789-01") - "12345678901" + '12345678901' >>> sieve("98/76.543-2101") - "98765432101" + '98765432101' .. note:: This method should not be used in new code and is only provided for @@ -47,9 +47,9 @@ def remove_symbols(dirty: str) -> str: Example: >>> remove_symbols("12.345/6789-01") - "12345678901" + '12345678901' >>> remove_symbols("98/76.543-2101") - "98765432101" + '98765432101' """ return sieve(dirty) @@ -75,9 +75,9 @@ def display(cnpj: str) -> str | None: Example: >>> display("12345678901234") - "12.345.678/9012-34" + '12.345.678/9012-34' >>> display("98765432100100") - "98.765.432/1001-00" + '98.765.432/1001-00' .. note:: This method should not be used in new code and is only provided for @@ -115,7 +115,6 @@ def format_cnpj(cnpj: str) -> str | None: >>> format_cnpj("03560714000142") '03.560.714/0001-42' >>> format_cnpj("98765432100100") - None """ if not is_valid(cnpj): @@ -232,12 +231,12 @@ def generate(branch: int | str = 1, alphanumeric: bool = False) -> str: str: A randomly generated valid CNPJ string. Example: - >>> generate() - "30180536000105" - >>> generate(1234) - "01745284123455" - >>> generate(branch="AB12", alphanumeric=True) - "NX9K79E2AB1200" + >>> generate() # doctest: +SKIP + '30180536000105' + >>> generate(1234) # doctest: +SKIP + '01745284123455' + >>> generate(branch="AB12", alphanumeric=True) # doctest: +SKIP + 'NX9K79E2AB1200' """ if alphanumeric: @@ -307,9 +306,9 @@ def _checksum(basenum: str) -> str: Example: >>> _checksum("123456789012") - "30" + '30' >>> _checksum("987654321001") - "41" + '41' """ verifying_digits = str(_hashdigit(basenum, 13)) diff --git a/brutils/cpf.py b/brutils/cpf.py index c2e418a..8654827 100644 --- a/brutils/cpf.py +++ b/brutils/cpf.py @@ -62,9 +62,9 @@ def display(cpf: str) -> str: Example: >>> display("12345678901") - "123.456.789-01" + '123.456.789-01' >>> display("98765432101") - "987.654.321-01" + '987.654.321-01' .. note:: This method should not be used in new code and is only provided for @@ -174,10 +174,10 @@ def generate() -> str: str: A random valid CPF string. Example: - >>> generate() - "10895948109" - >>> generate() - "52837606502" + >>> generate() # doctest: +SKIP + '10895948109' + >>> generate() # doctest: +SKIP + '52837606502' """ base = str(randint(1, 999999998)).zfill(9) diff --git a/brutils/currency.py b/brutils/currency.py index 4e96bcc..59ec7b9 100644 --- a/brutils/currency.py +++ b/brutils/currency.py @@ -21,7 +21,6 @@ def format_currency(value: float | int | str | Decimal) -> str | None: >>> format_currency(-9876.54) 'R$ -9.876,54' >>> format_currency("invalid") - None """ try: decimal_value = Decimal(value) diff --git a/brutils/ibge/municipality.py b/brutils/ibge/municipality.py index c875155..b324262 100644 --- a/brutils/ibge/municipality.py +++ b/brutils/ibge/municipality.py @@ -27,11 +27,10 @@ def get_municipality_by_code(code: str) -> tuple[str, str] | None: Example: >>> get_municipality_by_code("3550308") - ("São Paulo", "SP") + ('São Paulo', 'SP') >>> get_municipality_by_code("3304557") - ("Rio de Janeiro", "RJ") + ('Rio de Janeiro', 'RJ') >>> get_municipality_by_code("1234567") - None """ base_url = IBGE_MUNICIPALITY_BY_CODE_URL.format(code=code) @@ -68,11 +67,10 @@ def get_code_by_municipality_name( Example: >>> get_code_by_municipality_name("São Paulo", "SP") - "3550308" + '3550308' >>> get_code_by_municipality_name("Conceição do Coité", "Ba") - "2908408" + '2908408' >>> get_code_by_municipality_name("Municipio Inexistente", "RS") - None """ uf = uf.upper() diff --git a/brutils/legal_nature.py b/brutils/legal_nature.py index 64e488b..37676c1 100644 --- a/brutils/legal_nature.py +++ b/brutils/legal_nature.py @@ -149,7 +149,6 @@ def get_description(code: str) -> Optional[str]: >>> get_description("101-5") 'Órgão Público do Poder Executivo Federal' >>> get_description("0000") - None """ normalized = _normalize(code) return LEGAL_NATURE.get(normalized) if normalized else None diff --git a/brutils/legal_process.py b/brutils/legal_process.py index 53fdc00..d4e596c 100644 --- a/brutils/legal_process.py +++ b/brutils/legal_process.py @@ -53,7 +53,6 @@ def format_legal_process(legal_process_id: str) -> str | None: >>> format_legal_process("98765432109876543210") '9876543-21.0987.6.54.3210' >>> format_legal_process("123") - None """ if legal_process_id.isdigit() and len(legal_process_id) == 20: @@ -131,12 +130,11 @@ def generate( None if one of the arguments is invalid. Example: - >>> generate(2023, 5) + >>> generate(2023, 5) # doctest: +SKIP '51659517020235080562' - >>> generate() + >>> generate() # doctest: +SKIP '88031888120233030000' - >>> generate(2022, 10) - None + >>> generate(2022, 10) # doctest: +SKIP """ if year < datetime.now().year or orgao not in range(1, 10): diff --git a/brutils/license_plate.py b/brutils/license_plate.py index 2faa238..02d8266 100644 --- a/brutils/license_plate.py +++ b/brutils/license_plate.py @@ -24,7 +24,6 @@ def convert_to_mercosul(license_plate: str) -> str | None: >>> convert_to_mercosul("ABC4567") 'ABC4F67' >>> convert_to_mercosul("ABC4*67") - None """ if not _is_valid_old_format(license_plate): return None @@ -48,12 +47,11 @@ def format_license_plate(license_plate: str) -> str | None: input is invalid. Example: - >>> format("ABC1234") # old format (contains a dash) + >>> format_license_plate("ABC1234") # old format (contains a dash) 'ABC-1234' - >>> format("abc1e34") # mercosul format + >>> format_license_plate("abc1e34") # mercosul format 'ABC1E34' - >>> format("ABC123") - None + >>> format_license_plate("ABC123") """ license_plate = license_plate.upper() @@ -107,11 +105,11 @@ def remove_symbols(license_plate_number: str) -> str: Example: >>> remove_symbols("ABC-123") - "ABC123" + 'ABC123' >>> remove_symbols("abc123") - "abc123" + 'abc123' >>> remove_symbols("ABCD123") - "ABCD123" + 'ABCD123' """ return license_plate_number.replace("-", "") @@ -130,12 +128,11 @@ def get_format(license_plate: str) -> str | None: 'None' if the format is invalid. Example: - >>> get_format("abc123") - "LLLNNNN" + >>> get_format("abc1234") + 'LLLNNNN' >>> get_format("abc1d23") - "LLLNLNN" + 'LLLNLNN' >>> get_format("ABCD123") - None """ if _is_valid_old_format(license_plate): @@ -162,14 +159,13 @@ def generate(format: str = "LLLNLNN") -> str | None: 'None' if the format is invalid. Example: - >>> generate() - "ABC1D23" - >>> generate(format="LLLNLNN") - "ABC4D56" - >>> generate(format="LLLNNNN") - "ABC123" + >>> generate() # doctest: +SKIP + 'ABC1D23' + >>> generate(format="LLLNLNN") # doctest: +SKIP + 'ABC4D56' + >>> generate(format="LLLNNNN") # doctest: +SKIP + 'ABC123' >>> generate(format="invalid") - None """ generated = "" diff --git a/brutils/passport.py b/brutils/passport.py index 4c07781..2dbc18f 100644 --- a/brutils/passport.py +++ b/brutils/passport.py @@ -18,7 +18,7 @@ def is_valid(passport: str) -> bool: bool: True if the passport number is valid (2 letters followed by 6 digits). False otherwise. Example: - >>> is_valid("Ab123456") + >>> is_valid("AB123456") True >>> is_valid("12345678") False @@ -49,11 +49,11 @@ def remove_symbols(passport: str) -> str: Example: >>> remove_symbols("Ab123456") - Ab123456 + 'Ab123456' >>> remove_symbols("Ab-123456") - Ab123456 + 'Ab123456' >>> remove_symbols("Ab -. 123456") - Ab123456 + 'Ab123456' """ return "".join(filter(lambda c: c not in ".- ", passport)) @@ -73,11 +73,10 @@ def format_passport(passport: str) -> str | None: Example: >>> format_passport("Ab123456") - AB123456 + 'AB123456' >>> format_passport("Ab-123456") - AB123456 + 'AB123456' >>> format_passport("111111") - None """ passport = remove_symbols(passport.upper()) @@ -95,10 +94,10 @@ def generate() -> str: str: A random valid passport number string. Example: - >>> generate() - "RY393097" - >>> generate() - "ZS840088" + >>> generate() # doctest: +SKIP + 'RY393097' + >>> generate() # doctest: +SKIP + 'ZS840088' """ letters = "".join(random.choices(string.ascii_uppercase, k=2)) diff --git a/brutils/phone.py b/brutils/phone.py index 46b063d..0552993 100644 --- a/brutils/phone.py +++ b/brutils/phone.py @@ -92,12 +92,12 @@ def generate(type: str = None) -> str: str: A randomly generated valid phone number. Example: - >>> generate() - "2234451215" - >>> generate("mobile") - "1899115895" - >>> generate("landline") - "5535317900" + >>> generate() # doctest: +SKIP + '2234451215' + >>> generate("mobile") # doctest: +SKIP + '1899115895' + >>> generate("landline") # doctest: +SKIP + '5535317900' """ if type == "mobile": diff --git a/brutils/pis.py b/brutils/pis.py index 723aca7..08eb659 100644 --- a/brutils/pis.py +++ b/brutils/pis.py @@ -44,10 +44,10 @@ def format_pis(pis: str) -> str: or None if the input is invalid. Example: - >>> format_pis("12345678909") - '123.45678.90-9' - >>> format_pis("98765432100") - '987.65432.10-0' + >>> format_pis("45175440093") + '451.75440.09-3' + >>> format_pis("31313213712") + '313.13213.71-2' """ if not is_valid(pis): @@ -72,10 +72,10 @@ def is_valid(pis: str) -> bool: bool: True if PIS is valid, False otherwise. Example: - >>> is_valid_pis("82178537464") - True - >>> is_valid_pis("55550207753") + >>> is_valid("45175440093") True + >>> is_valid("55550207753") + False """ @@ -102,9 +102,9 @@ def generate() -> str: str: A randomly generated valid PIS number as a string. Example: - >>> generate() + >>> generate() # doctest: +SKIP '12345678909' - >>> generate() + >>> generate() # doctest: +SKIP '98765432100' """ base = str(randint(0, 9999999999)).zfill(10) From de50cd3099f4453138a83ddc02340f4f2e71a0d7 Mon Sep 17 00:00:00 2001 From: Pablo Aguilar Date: Sat, 29 Aug 2026 02:17:25 -0300 Subject: [PATCH 03/12] fix: fix examples in both readmes --- README.md | 406 +++++++++++++++++++++++++++----------------------- README_EN.md | 413 ++++++++++++++++++++++++++++----------------------- 2 files changed, 448 insertions(+), 371 deletions(-) diff --git a/README.md b/README.md index 86dad48..ae75f70 100644 --- a/README.md +++ b/README.md @@ -40,10 +40,11 @@ pip install brutils Para usar um de nossos utilitários, basta importar a função necessária, como no exemplo abaixo: -```python +```pycon >>> from brutils import is_valid_cpf >>> is_valid_cpf('00011122233') False + ``` # Utilitários @@ -139,12 +140,13 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import is_valid_cpf >>> is_valid_cpf("82178537464") True >>> is_valid_cpf('00011122233') False + ``` ### format_cpf @@ -164,12 +166,13 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import format_cpf >>> format_cpf('82178537464') '821.785.374-64' >>> format_cpf("55550207753") '555.502.077-53' + ``` ### remove_symbols_cpf @@ -188,10 +191,11 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import remove_symbols_cpf >>> remove_symbols_cpf('000.111.222-33') '00011122233' + ``` ### generate_cpf @@ -204,12 +208,13 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import generate_cpf ->>> generate_cpf() +>>> generate_cpf() # doctest: +SKIP '17433964657' ->>> generate_cpf() -"10895948109" +>>> generate_cpf() # doctest: +SKIP +'10895948109' + ``` ## CNPJ @@ -233,12 +238,13 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import is_valid_cnpj >>> is_valid_cnpj('03560714000142') True >>> is_valid_cnpj('00111222000133') False + ``` ### format_cnpj @@ -258,12 +264,12 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import format_cnpj >>> format_cnpj("03560714000142") '03.560.714/0001-42' >>> format_cnpj("98765432100100") -None + ``` ### remove_symbols_cnpj @@ -283,10 +289,11 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import remove_symbols_cnpj >>> remove_symbols_cnpj('00.111.222/0001-00') '00111222000100' + ``` ### generate_cnpj @@ -308,19 +315,20 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import generate_cnpj ->>> generate_cnpj() +>>> generate_cnpj() # doctest: +SKIP '34665388000161' ->>> generate_cnpj(1234) -"01745284123455" ->>> generate_cnpj(alphanumeric=True) -"9359QAG9000184" ->>> generate_cnpj(branch="AB12", alphanumeric=True) -"BR2026UTAB1290" ->>> generate_cnpj(branch="CD34", alphanumeric=True) +>>> generate_cnpj(1234) # doctest: +SKIP +'01745284123455' +>>> generate_cnpj(alphanumeric=True) # doctest: +SKIP +'9359QAG9000184' +>>> generate_cnpj(branch="AB12", alphanumeric=True) # doctest: +SKIP +'BR2026UTAB1290' +>>> generate_cnpj(branch="CD34", alphanumeric=True) # doctest: +SKIP # CNPJ inválido para exemplos de validação: -"NX9K79E2CD3400" +'NX9K79E2CD3400' + ``` ## CEP @@ -342,7 +350,7 @@ Retorno: Exemplo: -```python +```pycon >>> from brutils import is_valid_cep >>> is_valid_cep('01310200') True @@ -350,6 +358,7 @@ True False >>> is_valid_cep("abcdefgh") False + ``` ### format_cep @@ -370,14 +379,14 @@ Retorna: Example: -```python +```pycon >>> from brutils import format_cep >>> format_cep('01310200') '01310-200' >>> format_cep("12345678") -"12345-678" +'12345-678' >>> format_cep("12345") -None + ``` ### remove_symbols_cep @@ -397,14 +406,15 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import remove_symbols_cep >>> remove_symbols_cep('01310-200') '01310200' >>> remove_symbols_cep("123-45.678.9") -"123456789" +'123456789' >>> remove_symbols_cep("abc.xyz") -"abcxyz" +'abcxyz' + ``` ### generate_cep @@ -418,12 +428,13 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import generate_cep ->>> generate_cep() +>>> generate_cep() # doctest: +SKIP '77520503' ->>> generate_cep() +>>> generate_cep() # doctest: +SKIP '29641407' + ``` ### get_address_from_cep @@ -441,9 +452,9 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import get_address_from_cep ->>> get_address_from_cep("12345678") +>>> get_address_from_cep("12345678") # doctest: +SKIP { "cep": "12345-678", "logradouro": "Rua Example", @@ -456,6 +467,7 @@ Exemplo: "ddd": "12", "siafi": "1234" } + ``` ### get_cep_information_from_address @@ -475,23 +487,24 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import get_cep_information_from_address ->>> get_cep_information_from_address("EX", "Example", "Rua Example") +>>> get_cep_information_from_address("SP", "São Paulo", "Tua Thomaz Gonzaga") # doctest: +SKIP [ { - "cep": "12345-678", - "logradouro": "Rua Example", - "complemento": "", - "bairro": "Example", - "localidade": "Example", - "uf": "EX", - "ibge": "1234567", - "gia": "1234", - "ddd": "12", - "siafi": "1234" + 'cep': '01506-020', + 'logradouro': 'Rua Thomaz Gonzaga', + 'complemento': '', + 'bairro': 'Liberdade', + 'localidade': 'São Paulo', + 'uf': 'SP', + 'ibge': '3550308', + 'gia': '1004', + 'ddd': '11', + 'siafi': '7107' } ] + ``` ## Telefone @@ -528,7 +541,7 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import is_valid_phone >>> is_valid_phone('11994029275') True @@ -536,6 +549,7 @@ True True >>> is_valid_phone('1938814933', 'landline') True + ``` ### format_phone @@ -552,14 +566,14 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import format_phone >>> format_phone("11994029275") '(11)99402-9275' >>> format_phone("1635014415") '(16)3501-4415' >>> format_phone("333333") -None + ``` ### remove_symbols_phone @@ -576,7 +590,7 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import remove_symbols_phone >>> remove_symbols_phone('(21)2569-6969') '2125696969' @@ -584,6 +598,7 @@ Exemplo: '1199998888' >>> remove_symbols_phone('333333') '333333' + ``` ### remove_international_dialing_code @@ -600,18 +615,19 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import remove_international_dialing_code >>> remove_international_dialing_code("5521994029275") -"21994029275" +'21994029275' >>> remove_international_dialing_code("+5521994029275") -"+21994029275" +'+21994029275' >>> remove_international_dialing_code("5555994029275") -"55994029275" +'55994029275' >>> remove_international_dialing_code("21994029275") -"21994029275" +'21994029275' >>> remove_international_dialing_code("(+55)21994029275") -"(+)21994029275" +'(+)21994029275' + ``` ### generate_phone @@ -630,14 +646,15 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import generate_phone ->>> generate_phone() -"5929797740" ->>> generate_phone("mobile") -"1899115895" ->>> generate_phone("landline") -"5535317900" +>>> generate_phone() # doctest: +SKIP +'5929797740' +>>> generate_phone("mobile") # doctest: +SKIP +'1899115895' +>>> generate_phone("landline") # doctest: +SKIP +'5535317900' + ``` ## Email @@ -657,9 +674,8 @@ Retorna: Exemplo: -```python -from brutils import is_valid_email - +```pycon +>>> from brutils import is_valid_email >>> is_valid_email("joao.ninguem@gmail.com") True >>> is_valid_email(".joao.ninguem@gmail.com") @@ -668,6 +684,7 @@ False False >>> is_valid_email("joao ninguem@gmail.com") False + ``` ## Data @@ -684,16 +701,16 @@ Retorna: Exemplo: -````python +````pycon >>> from brutils import convert_date_to_text >>> convert_date_to_text("25/12/2000") -"Vinte e cinco de dezembro de dois mil" +'Vinte e cinco de dezembro de dois mil' >>> convert_date_to_text("31/02/2000") -None >>> convert_date_to_text("29/02/2024") -"Vinte e nove de fevereiro de dois mil e vinte e quatro" ->>> convert_date_to_text("1/08/2024") -"Primeiro de agosto de dois mil e vinte e quatro" +'Vinte e nove de fevereiro de dois mil e vinte e quatro' +>>> convert_date_to_text("01/08/2024") +'Primeiro de agosto de dois mil e vinte e quatro' + ```` ## CNH @@ -715,7 +732,7 @@ Retorno: Exemplo: -```python +```pycon >>> from brutils import is_valid_cnh >>> is_valid_cnh("12345678901") False @@ -725,6 +742,7 @@ False True >>> is_valid_cnh("987654321-00") True + ``` @@ -748,7 +766,7 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import is_valid_license_plate >>> is_valid_license_plate('ABC1234') True @@ -760,6 +778,7 @@ True True >>> is_valid_license_plate('GHI-4567') False + ``` ### format_license_plate @@ -778,22 +797,22 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import format_license_plate >>> format_license_plate("ABC1234") -"ABC-1234" +'ABC-1234' + # formato antigo (contém traço) >>> format_license_plate("abc1234") -"ABC-1234" -# formato antigo (contém traço) +'ABC-1234' >>> format_license_plate("ABC1D23") -"ABC1D23" +'ABC1D23' + # formato mercosul >>> format_license_plate("abc1d23") -"ABC1D23" -# formato mercosul +'ABC1D23' >>> format_license_plate("ABCD123") -None + ``` ### remove_symbols_license_plate @@ -810,16 +829,17 @@ Retorna: Exemplo: -```python -from brutils import remove_symbols_license_plate +```pycon +>>> from brutils import remove_symbols_license_plate >>> remove_symbols_license_plate("ABC-123") -"ABC123" +'ABC123' >>> remove_symbols_license_plate("abc123") -"abc123" +'abc123' >>> remove_symbols_license_plate("ABCD123") -"ABCD123" +'ABCD123' >>> remove_symbols_license_plate("@abc#-#123@") -"@abc##123@" +'@abc##123@' + ``` ### generate_license_plate @@ -837,16 +857,17 @@ Retorna: Exemplo: -```python -from brutils import generate_license_plate ->>> generate_license_plate() -"ABC1D23" ->>> generate_license_plate(format="LLLNLNN") -"ABC4D56" ->>> generate_license_plate(format="LLLNNNN") -"ABC123" ->>> generate_license_plate(format="invalid") +```pycon +>>> from brutils import generate_license_plate +>>> generate_license_plate() # doctest: +SKIP +'ABC1D23' +>>> generate_license_plate(format="LLLNLNN") # doctest: +SKIP +'ABC4D56' +>>> generate_license_plate(format="LLLNNNN") # doctest: +SKIP +'ABC123' +>>> generate_license_plate(format="invalid") # doctest: +SKIP None + ``` ### convert_license_plate_to_mercosul @@ -863,14 +884,14 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import convert_license_plate_to_mercosul ->>> convert_license_plate_to_mercosul("ABC123") -"ABC1C34" ->>> convert_license_plate_to_mercosul("abc123") -"ABC1C34" +>>> convert_license_plate_to_mercosul("ABC1234") +'ABC1C34' +>>> convert_license_plate_to_mercosul("abc1234") +'ABC1C34' >>> convert_license_plate_to_mercosul("ABC1D23") -None + ``` ### get_format_license_plate @@ -889,18 +910,18 @@ Retorna: Exemplo: -```python -from brutils import get_format_license_plate ->>> get_format_license_plate("ABC123") -"LLLNNNN" ->>> get_format_license_plate("abc123") -"LLLNNNN" +```pycon +>>> from brutils import get_format_license_plate +>>> get_format_license_plate("ABC1234") +'LLLNNNN' +>>> get_format_license_plate("abc1234") +'LLLNNNN' >>> get_format_license_plate("ABC1D23") -"LLLNLNN" +'LLLNLNN' >>> get_format_license_plate("abc1d23") -"LLLNLNN" +'LLLNLNN' >>> get_format_license_plate("ABCD123") -None + ``` ## PIS @@ -923,12 +944,13 @@ Retorna: Exemplo: -```python -from brutils import is_valid_pis +```pycon +>>> from brutils import is_valid_pis >>> is_valid_pis("82178537464") False >>> is_valid_pis("12082043519") True + ``` ### format_pis @@ -945,12 +967,13 @@ Retorna: Exemplo: -```python -from brutils import format_pis +```pycon +>>> from brutils import format_pis >>> format_pis("17033259504") '170.33259.50-4' >>> format_pis("12013128292") '120.13128.29-2' + ``` ### remove_symbols_pis @@ -967,8 +990,8 @@ Retorna: Exemplo: -```python -from brutils import remove_symbols_pis +```pycon +>>> from brutils import remove_symbols_pis >>> remove_symbols_pis('170.33259.50-4') '17033259504' @@ -976,6 +999,7 @@ from brutils import remove_symbols_pis '12345678909' >>> remove_symbols_pis('/._') '/_' + ``` ### generate_pis @@ -988,12 +1012,13 @@ Retorna: Exemplo: -```python -from brutils import generate_pis ->>> generate_pis() +```pycon +>>> from brutils import generate_pis +>>> generate_pis() # doctest: +SKIP '61352489741' ->>> generate_pis() +>>> generate_pis() # doctest: +SKIP '73453349671' + ``` ## Processo Jurídico @@ -1015,7 +1040,7 @@ Retorna: Examplo: -```python +```pycon >>> from brutils import is_valid_legal_process >>> is_valid_legal_process('10188748220234018200') True @@ -1025,7 +1050,7 @@ True False >>> is_valid_legal_process('455323423QQWEQWSsasd&*(()') False ->>> + ``` ### format_legal_process @@ -1043,13 +1068,13 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import format_legal_process >>> format_legal_process('23141945820055070079') '2314194-58.2005.5.07.0079' >>> format_legal_process('00000000000000000000') '0000000-00.0000.0.00.0000' ->>> + ``` ### remove_symbols_legal_process @@ -1070,14 +1095,15 @@ Retorna: Exemplo: -```python -from brutils import remove_symbols_legal_process +```pycon +>>> from brutils import remove_symbols_legal_process >>> remove_symbols_legal_process("6439067-89.2023.4.04.5902") -"64390678920234045902" +'64390678920234045902' >>> remove_symbols_legal_process("4976023-82.2012.7.00.2263") -"49760238220127002263" +'49760238220127002263' >>> remove_symbols_legal_process("4976023-82.2012.7.00.2263*!*&#") -"49760238220127002263*!*&#" +'49760238220127002263*!*&#' + ``` ### generate_legal_process @@ -1098,16 +1124,17 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import generate_legal_process ->>> generate_legal_process() -"45676401020238170592" ->>> generate_legal_process(ano=2025) -"32110268020258121130" ->>> generate_legal_process(orgao=5) -"37573041520235090313" ->>> generate_legal_process(ano=2024, orgao=4) -"33158248820244017105" +>>> generate_legal_process() # doctest: +SKIP +'45676401020238170592' +>>> generate_legal_process(year=2025) # doctest: +SKIP +'32110268020258121130' +>>> generate_legal_process(orgao=5) # doctest: +SKIP +'37573041520235090313' +>>> generate_legal_process(year=2024, orgao=4) # doctest: +SKIP +'33158248820244017105' + ``` ## Titulo Eleitoral @@ -1131,12 +1158,13 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import is_valid_voter_id >>> is_valid_voter_id('123456789011') False >>> is_valid_voter_id('427503840213') True + ``` ### format_voter_id @@ -1156,14 +1184,14 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import format_voter_id >>> format_voter_id("246593980493") '2465 9398 04 93' >>> format_voter_id("202715292895") '2027 1529 28 95' >>> format_voter_id("739035552205") ->>> + ``` ### generate_voter_id @@ -1178,12 +1206,13 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import generate_voter_id ->>> generate_voter_id() +>>> generate_voter_id() # doctest: +SKIP '183475722801' ->>> generate_voter_id(federative_union ="MG") +>>> generate_voter_id(federative_union ="MG") # doctest: +SKIP '950125640248' + ``` ## IBGE @@ -1200,7 +1229,7 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils.ibge.uf import convert_code_to_uf >>> convert_code_to_uf("12") 'AC' @@ -1225,20 +1254,20 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import get_code_by_municipality_name >>> get_code_by_municipality_name("São Paulo", "SP") -"3550308" +'3550308' >>> get_code_by_municipality_name("goiania", "go") -"5208707" +'5208707' >>> get_code_by_municipality_name("Conceição do Coité", "BA") -"2908408" +'2908408' >>> get_code_by_municipality_name("conceicao do Coite", "Ba") -"2908408" +'2908408' >>> get_code_by_municipality_name("Municipio Inexistente", "") -None + >>> get_code_by_municipality_name("Municipio Inexistente", "RS") -None + ``` ### get_municipality_by_code @@ -1254,10 +1283,11 @@ Returns: Example: -```python +```pycon >>> from brutils import get_municipality_by_code ->>> get_municipality_by_code(3550308) -("São Paulo", "SP") +>>> get_municipality_by_code("3550308") +('São Paulo', 'SP') + ``` ### convert_uf_to_name @@ -1273,12 +1303,13 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils.ibge.uf import convert_uf_to_name >>> convert_uf_to_name('SP') 'São Paulo' >>> convert_uf_to_name('rj') 'Rio de Janeiro' + ``` ### convert_name_to_uf @@ -1294,7 +1325,7 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils.ibge.uf import convert_name_to_uf >>> convert_name_to_uf('São Paulo') 'SP' @@ -1327,7 +1358,7 @@ Retorna: Exemplo: -```python +```pycon >>> from datetime import datetime >>> from brutils import is_holiday @@ -1339,6 +1370,7 @@ False False >>> is_holiday(datetime(2024, 12, 25), uf="RJ") True + ``` ## Monetário @@ -1357,14 +1389,14 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils.currency import format_currency >>> format_currency(1259.03) 'R$ 1.259,03' >>> format_currency(0) 'R$ 0,00' >>> format_currency("not a number") -None + ``` ### convert_real_to_text @@ -1389,7 +1421,7 @@ Limitações: Exemplo: -```python +```pycon >>> from brutils.currency import convert_real_to_text >>> convert_real_to_text(1523.45) 'Mil, quinhentos e vinte e três reais e quarenta e cinco centavos' @@ -1400,7 +1432,7 @@ Exemplo: >>> convert_real_to_text(-50.25) 'Menos cinquenta reais e vinte e cinco centavos' >>> convert_real_to_text("invalid") -None + ``` ## Natureza Jurídica @@ -1417,7 +1449,7 @@ O valor é **normalizado** antes da checagem: remove espaços, mantém apenas d - `bool`: `True` se existir na tabela, `False` caso contrário. **Exemplo** -```python +```pycon >>> from brutils import legal_nature >>> legal_nature.is_valid("2062") True @@ -1425,6 +1457,7 @@ True True >>> legal_nature.is_valid("9999") False + ``` ### get_legal_nature_description @@ -1441,14 +1474,14 @@ Retorna a **descrição oficial** do código de Natureza Jurídica. Aceita `NNNN **Exemplo** -```python +```pycon >>> from brutils import legal_nature >>> legal_nature.get_description("2062") 'Sociedade Empresária Limitada' >>> legal_nature.get_description("101-5") 'Órgão Público do Poder Executivo Federal' >>> legal_nature.get_description("0000") -None + ``` ### list_all_legal_nature @@ -1461,13 +1494,14 @@ Retorna uma cópia do dicionário completo `{codigo: descricao}`. **Exemplo** -```python +```pycon >>> from brutils import legal_nature >>> data = legal_nature.list_all() >>> len(data) > 0 True >>> data["2062"] 'Sociedade Empresária Limitada' + ``` ## RENAVAM @@ -1489,12 +1523,13 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils import is_valid_renavam >>> is_valid_renavam("86769597308") True >>> is_valid_renavam("12345678901") False + ``` ## Passaporte @@ -1514,14 +1549,15 @@ Retorna: - bool: True se o número do passaporte for válido (2 letras seguidas de 6 dígitos). False caso contrário. Exemplo: -```python +```pycon >>> from brutils import is_valid_passport ->>> is_valid_passport("Ab123456") +>>> is_valid_passport("AB123456") True >>> is_valid_passport("12345678") False >>> is_valid_passport("DC-221345") False + ``` ### format_passport @@ -1536,14 +1572,14 @@ Retorna: - str: O número do passaporte formatado (maiúsculas, sem símbolos) ou None se a entrada for inválida Exemplo: -```python +```pycon >>> from brutils import format_passport >>> format_passport("Ab123456") -AB123456 +'AB123456' >>> format_passport("Ab-123456") -AB123456 +'AB123456' >>> format_passport("111111") -None + ``` ### remove_symbols_passport @@ -1558,14 +1594,15 @@ Retorna: - str: O número do passaporte com hífens (-), pontos (.) e espaços em branco ( ) removidos. Exemplo: -```python +```pycon >>> from brutils import remove_symbols_passport >>> remove_symbols_passport("Ab123456") -Ab123456 +'Ab123456' >>> remove_symbols_passport("Ab-123456") -Ab123456 +'Ab123456' >>> remove_symbols_passport("Ab -. 123456") -Ab123456 +'Ab123456' + ``` ### generate_passport @@ -1577,12 +1614,13 @@ Retorna: - str: Uma string com um número de passaporte válido aleatório. Exemplo: -```python +```pycon >>> from brutils import generate_passport ->>> generate_passport() -"RY393097" ->>> generate_passport() -"ZS840088" +>>> generate_passport() # doctest: +SKIP +'RY393097' +>>> generate_passport() # doctest: +SKIP +'ZS840088' + ``` # Novos Utilitários e Reportar Bugs diff --git a/README_EN.md b/README_EN.md index 0df33c5..9b17fd1 100644 --- a/README_EN.md +++ b/README_EN.md @@ -39,10 +39,11 @@ pip install brutils To use one of our utilities you just need to import the required function as in the example below: -```python +```pycon >>> from brutils import is_valid_cpf >>> is_valid_cpf('00011122233') False + ``` # Utilities @@ -139,12 +140,13 @@ Returns: Example: -```python +```pycon >>> from brutils import is_valid_cpf >>> is_valid_cpf("82178537464") True >>> is_valid_cpf('00011122233') False + ``` ### format_cpf @@ -164,12 +166,13 @@ Returns: Example: -```python +```pycon >>> from brutils import format_cpf >>> format_cpf('82178537464') '821.785.374-64' >>> format_cpf("55550207753") '555.502.077-53' + ``` ### remove_symbols_cpf @@ -188,10 +191,11 @@ Returns: Example: -```python +```pycon >>> from brutils import remove_symbols_cpf >>> remove_symbols_cpf('000.111.222-33') '00011122233' + ``` ### generate_cpf @@ -204,12 +208,13 @@ Returns: Example: -```python +```pycon >>> from brutils import generate_cpf ->>> generate_cpf() +>>> generate_cpf() # doctest: +SKIP '17433964657' ->>> generate_cpf() +>>> generate_cpf() # doctest: +SKIP "10895948109" + ``` ## CNPJ @@ -234,12 +239,13 @@ Returns: Example: -```python +```pycon >>> from brutils import is_valid_cnpj >>> is_valid_cnpj('03560714000142') True >>> is_valid_cnpj('00111222000133') False + ``` ### format_cnpj @@ -260,12 +266,12 @@ Returns: Example: -```python +```pycon >>> from brutils import format_cnpj >>> format_cnpj("03560714000142") '03.560.714/0001-42' >>> format_cnpj("98765432100100") -None + ``` ### remove_symbols_cnpj @@ -285,10 +291,11 @@ Returns: Example: -```python +```pycon >>> from brutils import remove_symbols_cnpj >>> remove_symbols_cnpj('00.111.222/0001-00') '00111222000100' + ``` ### generate_cnpj @@ -310,19 +317,18 @@ Returns: Example: -```python +```pycon >>> from brutils import generate_cnpj ->>> generate_cnpj() +>>> generate_cnpj() # doctest: +SKIP '34665388000161' ->>> generate_cnpj(1234) -"01745284123455" ->>> generate_cnpj(alphanumeric=True) -"9359QAG9000184" ->>> generate_cnpj(branch="AB12", alphanumeric=True) -"BR2026UTAB1290" ->>> generate_cnpj(branch="CD34", alphanumeric=True) -# Invalid CNPJ for validation examples: -"NX9K79E2CD3400" +>>> generate_cnpj(1234) # doctest: +SKIP +'01745284123455' +>>> generate_cnpj(alphanumeric=True) # doctest: +SKIP +'9359QAG9000184' +>>> generate_cnpj(branch="AB12", alphanumeric=True) # doctest: +SKIP +'BR2026UTAB1290' +>>> generate_cnpj(branch="CD34", alphanumeric=True) # doctest: +SKIP +'NX9K79E2CD3400' ``` ## CEP @@ -344,7 +350,7 @@ Returns: Example: -```python +```pycon >>> from brutils import is_valid_cep >>> is_valid_cep('01310200') True @@ -352,6 +358,7 @@ True False >>> is_valid_cep("abcdefgh") False + ``` ### format_cep @@ -370,14 +377,14 @@ Returns: Example: -```python +```pycon >>> from brutils import format_cep >>> format_cep('01310200') '01310-200' >>> format_cep("12345678") -"12345-678" +'12345-678' >>> format_cep("12345") -None + ``` ### remove_symbols_cep @@ -395,14 +402,15 @@ Returns: Example: -```python +```pycon >>> from brutils import remove_symbols_cep >>> remove_symbols_cep('01310-200') '01310200' >>> remove_symbols_cep("123-45.678.9") -"123456789" +'123456789' >>> remove_symbols_cep("abc.xyz") -"abcxyz" +'abcxyz' + ``` ### generate_cep @@ -415,10 +423,11 @@ Returns: Example: -```python +```pycon >>> from brutils import generate_cep ->>> generate_cep() +>>> generate_cep() # doctest: +SKIP '77520503' + ``` ### get_address_from_cep @@ -436,9 +445,9 @@ Returns: Example: -```python +```pycon >>> from brutils import get_address_from_cep ->>> get_address_from_cep("12345678") +>>> get_address_from_cep("12345678") # doctest: +SKIP { "cep": "12345-678", "logradouro": "Rua Example", @@ -470,23 +479,24 @@ Returns: Example: -```python +```pycon >>> from brutils import get_cep_information_from_address ->>> get_cep_information_from_address("EX", "Example", "Rua Example") +>>> get_cep_information_from_address("SP", "São Paulo", "Tua Thomaz Gonzaga") # doctest: +SKIP [ { - "cep": "12345-678", - "logradouro": "Rua Example", - "complemento": "", - "bairro": "Example", - "localidade": "Example", - "uf": "EX", - "ibge": "1234567", - "gia": "1234", - "ddd": "12", - "siafi": "1234" + 'cep': '01506-020', + 'logradouro': 'Rua Thomaz Gonzaga', + 'complemento': '', + 'bairro': 'Liberdade', + 'localidade': 'São Paulo', + 'uf': 'SP', + 'ibge': '3550308', + 'gia': '1004', + 'ddd': '11', + 'siafi': '7107' } ] + ``` ## Date @@ -503,16 +513,17 @@ Return: Example: -````python +````pycon >>> from brutils import convert_date_to_text >>> convert_date_to_text("25/12/2000") -"Vinte e cinco de dezembro de dois mil" +'Vinte e cinco de dezembro de dois mil' >>> convert_date_to_text("31/02/2000") -None + >>> convert_date_to_text("29/02/2024") -"Vinte e nove de fevereiro de dois mil e vinte e quatro" ->>> convert_date_to_text("1/08/2024") -"Primeiro de agosto de dois mil e vinte e quatro" +'Vinte e nove de fevereiro de dois mil e vinte e quatro' +>>> convert_date_to_text("01/08/2024") +'Primeiro de agosto de dois mil e vinte e quatro' + ```` ## Phone @@ -548,14 +559,15 @@ Returns: Example: -```python +```pycon >>> from brutils import is_valid_phone >>> is_valid_phone('11994029275') True ->>> is_valid_mobile_phone('11994029275', 'mobile') +>>> is_valid_phone('11994029275', 'mobile') True ->>> is_valid_landline_phone('1938814933', 'landline') +>>> is_valid_phone('1938814933', 'landline') True + ``` ### format_phone @@ -572,14 +584,14 @@ Returns: Example: -```python +```pycon >>> from brutils import format_phone >>> format_phone("11994029275") '(11)99402-9275' >>> format_phone("1635014415") '(16)3501-4415' >>> format_phone("333333") -None + ``` ### remove_symbols_phone @@ -596,7 +608,7 @@ Returns: Example: -```python +```pycon >>> from brutils import remove_symbols_phone >>> remove_symbols_phone('(21)2569-6969') '2125696969' @@ -604,6 +616,7 @@ Example: '1199998888' >>> remove_symbols_phone('333333') '333333' + ``` ### remove_international_dialing_code @@ -620,18 +633,19 @@ Returns: Example: -```python +```pycon >>> from brutils import remove_international_dialing_code >>> remove_international_dialing_code("5521994029275") -"21994029275" +'21994029275' >>> remove_international_dialing_code("+5521994029275") -"+21994029275" +'+21994029275' >>> remove_international_dialing_code("5555994029275") -"55994029275" +'55994029275' >>> remove_international_dialing_code("21994029275") -"21994029275" +'21994029275' >>> remove_international_dialing_code("(+55)21994029275") -"(+)21994029275" +'(+)21994029275' + ``` ### generate_phone @@ -650,14 +664,15 @@ Returns: Example: -```python +```pycon >>> from brutils import generate_phone ->>> generate_phone() -"5929797740" ->>> generate_phone("mobile") -"1899115895" ->>> generate_phone("landline") -"5535317900" +>>> generate_phone() # doctest: +SKIP +'5929797740' +>>> generate_phone("mobile") # doctest: +SKIP +'1899115895' +>>> generate_phone("landline") # doctest: +SKIP +'5535317900' + ``` ## Email @@ -676,8 +691,8 @@ Returns: Example: -```python -from brutils import is_valid_email +```pycon +>>> from brutils import is_valid_email >>> is_valid_email("joao.ninguem@gmail.com") True @@ -687,6 +702,7 @@ False False >>> is_valid_email("joao ninguem@gmail.com") False + ``` @@ -707,7 +723,7 @@ Retorno: Exemplo: -```python +```pycon >>> from brutils import is_valid_cnh >>> is_valid_cnh("123456789") False @@ -717,6 +733,7 @@ False True >>> is_valid_cnh("987654321-00") True + ``` @@ -740,7 +757,7 @@ Returns: Example: -```python +```pycon >>> from brutils import is_valid_license_plate >>> is_valid_license_plate('ABC1234') True @@ -752,6 +769,7 @@ True True >>> is_valid_license_plate('GHI-4567') False + ``` ### format_license_plate @@ -771,22 +789,22 @@ Returns: Example: -```python +```pycon >>> from brutils import format_license_plate >>> format_license_plate("ABC1234") -"ABC-1234" +'ABC-1234' + # old format (contains a dash) >>> format_license_plate("abc1234") -"ABC-1234" -# old format (contains a dash) +'ABC-1234' >>> format_license_plate("ABC1D23") -"ABC1D23" +'ABC1D23' + # mercosul format >>> format_license_plate("abc1d23") -"ABC1D23" -# mercosul format +'ABC1D23' >>> format_license_plate("ABCD123") -None + ``` ### remove_symbols_license_plate @@ -804,16 +822,17 @@ Returns: Example: -```python -from brutils import remove_symbols_license_plate +```pycon +>>> from brutils import remove_symbols_license_plate >>> remove_symbols_license_plate("ABC-123") -"ABC123" +'ABC123' >>> remove_symbols_license_plate("abc123") -"abc123" +'abc123' >>> remove_symbols_license_plate("ABCD123") -"ABCD123" +'ABCD123' >>> remove_symbols_license_plate("@abc#-#123@") -"@abc##123@" +'@abc##123@' + ``` ### generate_license_plate @@ -834,16 +853,16 @@ Returns: Example: -```python -from brutils import generate_license_plate ->>> generate_license_plate() -"ABC1D23" ->>> generate_license_plate(format="LLLNLNN") -"ABC4D56" ->>> generate_license_plate(format="LLLNNNN") -"ABC123" ->>> generate_license_plate(format="invalid") -None +```pycon +>>> from brutils import generate_license_plate +>>> generate_license_plate() # doctest: +SKIP +'ABC1D23' +>>> generate_license_plate(format="LLLNLNN") # doctest: +SKIP +'ABC4D56' +>>> generate_license_plate(format="LLLNNNN") # doctest: +SKIP +'ABC123' +>>> generate_license_plate(format="invalid") # doctest: +SKIP + ``` ### convert_license_plate_to_mercosul @@ -863,14 +882,14 @@ Returns: Example: -```python +```pycon >>> from brutils import convert_license_plate_to_mercosul ->>> convert_license_plate_to_mercosul("ABC123") -"ABC1C34" ->>> convert_license_plate_to_mercosul("abc123") -"ABC1C34" +>>> convert_license_plate_to_mercosul("ABC1234") +'ABC1C34' +>>> convert_license_plate_to_mercosul("abc1234") +'ABC1C34' >>> convert_license_plate_to_mercosul("ABC1D23") -None + ``` ### get_format_license_plate @@ -889,18 +908,18 @@ Returns: Example: -```python -from brutils import get_format_license_plate ->>> get_format_license_plate("ABC123") -"LLLNNNN" ->>> get_format_license_plate("abc123") -"LLLNNNN" +```pycon +>>> from brutils import get_format_license_plate +>>> get_format_license_plate("ABC1234") +'LLLNNNN' +>>> get_format_license_plate("abc1234") +'LLLNNNN' >>> get_format_license_plate("ABC1D23") -"LLLNLNN" +'LLLNLNN' >>> get_format_license_plate("abc1d23") -"LLLNLNN" +'LLLNLNN' >>> get_format_license_plate("ABCD123") -None + ``` ## PIS @@ -923,12 +942,13 @@ Returns: Example: -```python -from brutils import is_valid_pis +```pycon +>>> from brutils import is_valid_pis >>> is_valid_pis("82178537464") False >>> is_valid_pis("12082043519") True + ``` ### format_pis @@ -945,12 +965,13 @@ Returns: Example: -```python -from brutils import format_pis +```pycon +>>> from brutils import format_pis >>> format_pis("17033259504") '170.33259.50-4' >>> format_pis("12013128292") '120.13128.29-2' + ``` ### remove_symbols_pis @@ -967,14 +988,15 @@ Returns: Example: -```python -from brutils import remove_symbols_pis +```pycon +>>> from brutils import remove_symbols_pis >>> remove_symbols_pis('170.33259.50-4') '17033259504' >>> remove_symbols_pis("123.456.789-09") '12345678909' >>> remove_symbols_pis('/._') '/_' + ``` ### generate_pis @@ -987,12 +1009,13 @@ Returns: Example: -```python -from brutils import generate_pis ->>> generate_pis() +```pycon +>>> from brutils import generate_pis +>>> generate_pis() # doctest: +SKIP '61352489741' ->>> generate_pis() +>>> generate_pis() # doctest: +SKIP '73453349671' + ``` ## Legal Process @@ -1015,7 +1038,7 @@ Returns: Example: -```python +```pycon >>> from brutils import is_valid_legal_process >>> is_valid_legal_process('10188748220234018200') True @@ -1025,7 +1048,7 @@ True False >>> is_valid_legal_process('455323423QQWEQWSsasd&*(()') False ->>> + ``` ### format_legal_process @@ -1043,14 +1066,14 @@ Returns: Example: -```python +```pycon >>> from brutils import format_legal_process >>> format_legal_process('23141945820055070079') '2314194-58.2005.5.07.0079' >>> format_legal_process('00000000000000000000') '0000000-00.0000.0.00.0000' >>> format_legal_process("123") -None + ``` ### remove_symbols_legal_process @@ -1070,14 +1093,15 @@ Returns: Example: -```python -from brutils import remove_symbols_legal_process +```pycon +>>> from brutils import remove_symbols_legal_process >>> remove_symbols_legal_process("6439067-89.2023.4.04.5902") -"64390678920234045902" +'64390678920234045902' >>> remove_symbols_legal_process("4976023-82.2012.7.00.2263") -"49760238220127002263" +'49760238220127002263' >>> remove_symbols_legal_process("4976023-82.2012.7.00.2263*!*&#") -"49760238220127002263*!*&#" +'49760238220127002263*!*&#' + ``` ### generate_legal_process @@ -1099,16 +1123,17 @@ Returns: Example: -```python +```pycon >>> from brutils import generate_legal_process ->>> generate_legal_process() -"45676401020238170592" ->>> generate_legal_process(year=2025) -"32110268020258121130" ->>> generate_legal_process(orgao=5) -"37573041520235090313" ->>> generate_legal_process(year=2024, orgao=4) -"33158248820244017105" +>>> generate_legal_process() # doctest: +SKIP +'45676401020238170592' +>>> generate_legal_process(year=2025) # doctest: +SKIP +'32110268020258121130' +>>> generate_legal_process(orgao=5) # doctest: +SKIP +'37573041520235090313' +>>> generate_legal_process(year=2024, orgao=4) # doctest: +SKIP +'33158248820244017105' + ``` ## Voter ID @@ -1133,12 +1158,13 @@ Returns: Example: -```python +```pycon >>> from brutils import is_valid_voter_id >>> is_valid_voter_id('123456789011') False >>> is_valid_voter_id('427503840213') True + ``` ### format_voter_id @@ -1157,14 +1183,14 @@ Returns: Example: -```python +```pycon >>> from brutils import format_voter_id >>> format_voter_id("246593980493") '2465 9398 04 93' >>> format_voter_id("202715292895") '2027 1529 28 95' >>> format_voter_id("739035552205") ->>> + ``` ### generate_voter_id @@ -1179,12 +1205,13 @@ Returns: Example: -```python +```pycon >>> from brutils import generate_voter_id ->>> generate_voter_id() +>>> generate_voter_id() # doctest: +SKIP '183475722801' ->>> generate_voter_id(federative_union ="MG") +>>> generate_voter_id(federative_union="MG") # doctest: +SKIP '950125640248' + ``` ## IBGE @@ -1200,14 +1227,14 @@ Retorna: Exemplo: -```python +```pycon >>> from brutils.ibge.uf import convert_code_to_uf >>> convert_code_to_uf("12") 'AC' >>> convert_code_to_uf("33") 'RJ' >>> convert_code_to_uf("99") ->>> + ``` ### get_code_by_municipality_name @@ -1228,20 +1255,20 @@ Returns: Example: -```python +```pycon >>> from brutils import get_code_by_municipality_name >>> get_code_by_municipality_name("São Paulo", "SP") -"3550308" +'3550308' >>> get_code_by_municipality_name("goiania", "go") -"5208707" +'5208707' >>> get_code_by_municipality_name("Conceição do Coité", "BA") -"2908408" +'2908408' >>> get_code_by_municipality_name("conceicao do Coite", "Ba") -"2908408" +'2908408' >>> get_code_by_municipality_name("Municipio Inexistente", "") -None + >>> get_code_by_municipality_name("Municipio Inexistente", "RS") -None + ``` ### get_municipality_by_code @@ -1257,10 +1284,11 @@ Returns: Example: -```python +```pycon >>> from brutils import get_municipality_by_code ->>> get_municipality_by_code(3550308) -("São Paulo", "SP") +>>> get_municipality_by_code("3550308") +('São Paulo', 'SP') + ``` ### convert_uf_to_name @@ -1276,12 +1304,13 @@ Returns: Example: -```python +```pycon >>> from brutils.ibge.uf import convert_uf_to_name >>> convert_uf_to_name('SP') 'São Paulo' >>> convert_uf_to_name('rj') 'Rio de Janeiro' + ``` ### convert_name_to_uf @@ -1297,7 +1326,7 @@ Returns: Example: -```python +```pycon >>> from brutils.ibge.uf import convert_name_to_uf >>> convert_name_to_uf('São Paulo') 'SP' @@ -1308,7 +1337,7 @@ Example: >>> convert_name_to_uf('rio de janeiro') 'RJ' >>> convert_name_to_uf('Invalid State') ->>> + ``` ## Holidays @@ -1330,7 +1359,7 @@ Returns: Example: -```python +```pycon >>> from datetime import datetime >>> from brutils import is_holiday >>> is_holiday(datetime(2024, 1, 1)) @@ -1341,6 +1370,7 @@ False False >>> is_holiday(datetime(2024, 12, 25), uf="RJ") True + ``` ## Monetary @@ -1359,14 +1389,14 @@ Returns: Example: -```python +```pycon >>> from brutils.currency import format_currency >>> format_currency(1259.03) 'R$ 1.259,03' >>> format_currency(0) 'R$ 0,00' >>> format_currency("not a number") -None + ``` ### convert_real_to_text @@ -1393,7 +1423,7 @@ Limitations: Example: -```python +```pycon >>> from brutils.currency import convert_real_to_text >>> convert_real_to_text(1523.45) 'Mil, quinhentos e vinte e três reais e quarenta e cinco centavos' @@ -1404,7 +1434,7 @@ Example: >>> convert_real_to_text(-50.25) 'Menos cinquenta reais e vinte e cinco centavos' >>> convert_real_to_text("invalid") -None + ``` ## Legal Nature @@ -1421,7 +1451,7 @@ The value is **normalized** before checking: removes spaces, keeps only digits, - `bool`: `True` if it exists in the table, `False` otherwise. **Example** -```python +```pycon >>> from brutils import legal_nature >>> legal_nature.is_valid("2062") True @@ -1429,6 +1459,7 @@ True True >>> legal_nature.is_valid("9999") False + ``` ### get_legal_nature_description @@ -1445,14 +1476,14 @@ Returns the **official description** for the Legal Nature code. Accepts `NNNN` o **Example** -```python +```pycon >>> from brutils import legal_nature >>> legal_nature.get_description("2062") 'Sociedade Empresária Limitada' >>> legal_nature.get_description("101-5") 'Órgão Público do Poder Executivo Federal' >>> legal_nature.get_description("0000") -None + ``` ### list_all_legal_nature @@ -1465,13 +1496,14 @@ Returns a copy of the complete dictionary `{code: description}`. **Example** -```python +```pycon >>> from brutils import legal_nature >>> data = legal_nature.list_all() >>> len(data) > 0 True >>> data["2062"] 'Sociedade Empresária Limitada' + ``` ## RENAVAM @@ -1492,12 +1524,13 @@ Returns: Example: -```python +```pycon >>> from brutils import is_valid_renavam >>> is_valid_renavam("86769597308") True >>> is_valid_renavam("12345678901") False + ``` ## Passport @@ -1517,14 +1550,15 @@ Returns: - bool: True if the passport number is valid (2 letters followed by 6 digits). False otherwise. Example: -```python +```pycon >>> from brutils import is_valid_passport ->>> is_valid_passport("Ab123456") +>>> is_valid_passport("AB123456") True >>> is_valid_passport("12345678") False >>> is_valid_passport("DC-221345") False + ``` ### format_passport @@ -1539,13 +1573,14 @@ Returns: - str: The formatted passport number (uppercase, without symbols) or None if the input is invalid Example: -```python +```pycon +>>> from brutils import format_passport >>> format_passport("Ab123456") -AB123456 +'AB123456' >>> format_passport("Ab-123456") -AB123456 +'AB123456' >>> format_passport("111111") -None + ``` ### remove_symbols_passport @@ -1562,13 +1597,15 @@ Returns: - str: The passport numbers with dashes (-), dots (.), and whitespaces ( ) removed. Example: -```python +```pycon +>>> from brutils import remove_symbols_passport >>> remove_symbols_passport("Ab123456") -Ab123456 +'Ab123456' >>> remove_symbols_passport("Ab-123456") -Ab123456 +'Ab123456' >>> remove_symbols_passport("Ab -. 123456") -Ab123456 +'Ab123456' + ``` ### generate_passport @@ -1581,11 +1618,13 @@ Returns: - str: A random valid passport number string. Example: -```python ->>> generate() -"RY393097" ->>> generate() -"ZS840088" +```pycon +>>> from brutils import generate_passport +>>> generate_passport() # doctest: +SKIP +'RY393097' +>>> generate_passport() # doctest: +SKIP +'ZS840088' + ``` # Feature Request and Bug Report From d57af23c8ff24c105511388418e7f32a9d3f29ff Mon Sep 17 00:00:00 2001 From: Pablo Aguilar Date: Sat, 29 Aug 2026 02:24:04 -0300 Subject: [PATCH 04/12] chore: use non deprecated parameter --- brutils/date_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brutils/date_utils.py b/brutils/date_utils.py index 960e5fb..b6aac82 100644 --- a/brutils/date_utils.py +++ b/brutils/date_utils.py @@ -58,7 +58,7 @@ def is_holiday(target_date: datetime, uf: str = None) -> bool | None: if uf is None: return target_date in national_holidays - state_holidays = holidays.Brazil(prov=uf, years=target_date.year) + state_holidays = holidays.Brazil(subdiv=uf, years=target_date.year) return target_date in state_holidays From 831c7f7218f84fd1a1f379914fcd014b0e6921c4 Mon Sep 17 00:00:00 2001 From: Pablo Aguilar Date: Sat, 29 Aug 2026 02:36:06 -0300 Subject: [PATCH 05/12] feat: improve test workflow --- .github/workflows/run-tests.yml | 34 +++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 8a2f266..1b0731e 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,31 +1,53 @@ name: run-tests + on: pull_request: push: branches: - main + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }} + cancel-in-progress: true + jobs: build: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.10', '3.11', '3.12', '3.13'] + python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] steps: - - uses: actions/checkout@v4 + - name: Checkout + uses: actions/checkout@v6 + - name: Set Up Python ${{ matrix.python-version }} uses: actions/setup-python@v7 with: python-version: ${{ matrix.python-version }} + - name: Poetry Setup uses: snok/install-poetry@v1 + with: + virtualenvs-create: true + virtualenvs-in-project: true + - name: Install Dependencies - run: poetry install - - name: Generate Report + run: poetry install --all-groups + + - name: Run tests run: | - poetry run coverage run -m unittest discover -s tests - poetry run coverage xml + poetry run pytest + poetry check + poetry run pip check + - name: Upload Coverage to Codecov uses: codecov/codecov-action@v7 + # Different Python versions are covered differently, + # so we only upload coverage for the latest version. + if: matrix.python-version == '3.14' with: files: ./coverage.xml token: ${{ secrets.CODECOV_TOKEN }} From dd8dbd037dea42a5f9a1b6badf81665d22b90f82 Mon Sep 17 00:00:00 2001 From: Pablo Aguilar Date: Sat, 29 Aug 2026 02:39:51 -0300 Subject: [PATCH 06/12] chore: update unittest references to pytest --- CONTRIBUTING.md | 4 ++-- Makefile | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 61b6a21..b50814f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -223,9 +223,9 @@ No arquivo `Makefile` do seu projeto, verifique se a seção de testes está con ```makefile test: ifeq ($(OS),Windows_NT) - @set PYTHONDONTWRITEBYTECODE=1 && poetry run python -m unittest discover tests/ -v + @set PYTHONDONTWRITEBYTECODE=1 && poetry run python pytest else - @PYTHONDONTWRITEBYTECODE=1 poetry run python3 -m unittest discover tests/ -v + @PYTHONDONTWRITEBYTECODE=1 poetry run pytest endif ``` diff --git a/Makefile b/Makefile index 6c9f4e5..9e4cda4 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ check: test: ifeq ($(OS),Windows_NT) - @set PYTHONDONTWRITEBYTECODE=1 && poetry run python -m unittest discover tests/ -v + @set PYTHONDONTWRITEBYTECODE=1 && poetry run pytest else - @PYTHONDONTWRITEBYTECODE=1 poetry run python3 -m unittest discover tests/ -v -endif \ No newline at end of file + @PYTHONDONTWRITEBYTECODE=1 poetry run pytest +endif From 2f201b0111fe117797f5020f52bb719cdb3e54d3 Mon Sep 17 00:00:00 2001 From: Pablo Aguilar Date: Sat, 29 Aug 2026 02:47:40 -0300 Subject: [PATCH 07/12] chore: prevent doctests that make requests to run --- README.md | 14 +++++++------- README_EN.md | 14 +++++++------- brutils/cep.py | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index ae75f70..d3b8494 100644 --- a/README.md +++ b/README.md @@ -1256,17 +1256,17 @@ Exemplo: ```pycon >>> from brutils import get_code_by_municipality_name ->>> get_code_by_municipality_name("São Paulo", "SP") +>>> get_code_by_municipality_name("São Paulo", "SP") # doctest: +SKIP '3550308' ->>> get_code_by_municipality_name("goiania", "go") +>>> get_code_by_municipality_name("goiania", "go") # doctest: +SKIP '5208707' ->>> get_code_by_municipality_name("Conceição do Coité", "BA") +>>> get_code_by_municipality_name("Conceição do Coité", "BA") # doctest: +SKIP '2908408' ->>> get_code_by_municipality_name("conceicao do Coite", "Ba") +>>> get_code_by_municipality_name("conceicao do Coite", "Ba") # doctest: +SKIP '2908408' ->>> get_code_by_municipality_name("Municipio Inexistente", "") +>>> get_code_by_municipality_name("Municipio Inexistente", "") # doctest: +SKIP ->>> get_code_by_municipality_name("Municipio Inexistente", "RS") +>>> get_code_by_municipality_name("Municipio Inexistente", "RS") # doctest: +SKIP ``` @@ -1285,7 +1285,7 @@ Example: ```pycon >>> from brutils import get_municipality_by_code ->>> get_municipality_by_code("3550308") +>>> get_municipality_by_code("3550308") # doctest: +SKIP ('São Paulo', 'SP') ``` diff --git a/README_EN.md b/README_EN.md index 9b17fd1..0616a73 100644 --- a/README_EN.md +++ b/README_EN.md @@ -1257,17 +1257,17 @@ Example: ```pycon >>> from brutils import get_code_by_municipality_name ->>> get_code_by_municipality_name("São Paulo", "SP") +>>> get_code_by_municipality_name("São Paulo", "SP") # doctest: +SKIP '3550308' ->>> get_code_by_municipality_name("goiania", "go") +>>> get_code_by_municipality_name("goiania", "go") # doctest: +SKIP '5208707' ->>> get_code_by_municipality_name("Conceição do Coité", "BA") +>>> get_code_by_municipality_name("Conceição do Coité", "BA") # doctest: +SKIP '2908408' ->>> get_code_by_municipality_name("conceicao do Coite", "Ba") +>>> get_code_by_municipality_name("conceicao do Coite", "Ba") # doctest: +SKIP '2908408' ->>> get_code_by_municipality_name("Municipio Inexistente", "") +>>> get_code_by_municipality_name("Municipio Inexistente", "") # doctest: +SKIP ->>> get_code_by_municipality_name("Municipio Inexistente", "RS") +>>> get_code_by_municipality_name("Municipio Inexistente", "RS") # doctest: +SKIP ``` @@ -1286,7 +1286,7 @@ Example: ```pycon >>> from brutils import get_municipality_by_code ->>> get_municipality_by_code("3550308") +>>> get_municipality_by_code("3550308") # doctest: +SKIP ('São Paulo', 'SP') ``` diff --git a/brutils/cep.py b/brutils/cep.py index 40ba177..371120d 100644 --- a/brutils/cep.py +++ b/brutils/cep.py @@ -151,7 +151,7 @@ def get_address_from_cep( Traceback (most recent call last): brutils.exceptions.cep.InvalidCEP: CEP 'abcdefg' is invalid. - >>> get_address_from_cep("00000000", True) + >>> get_address_from_cep("00000000", True) # doctest: +SKIP Traceback (most recent call last): brutils.exceptions.cep.CEPNotFound: 00000000 """ From 85b0d83bdbe7aeb47937dd74b4a129bce93810bf Mon Sep 17 00:00:00 2001 From: Pablo Aguilar Date: Sat, 29 Aug 2026 03:23:05 -0300 Subject: [PATCH 08/12] chore: configure tests workflow cache --- .github/workflows/run-tests.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 1b0731e..99bd4f9 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -8,6 +8,7 @@ on: permissions: contents: read + actions: write concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }} @@ -34,8 +35,16 @@ jobs: virtualenvs-create: true virtualenvs-in-project: true + - name: Cache dependencies + id: cache-deps + uses: actions/cache@v6 + with: + path: .venv + key: pydeps-${{ runner.os }}-python-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }} + - name: Install Dependencies - run: poetry install --all-groups + if: steps.cache-deps.outputs.cache-hit != 'true' + run: poetry install --no-interaction --all-groups - name: Run tests run: | From f71cc63a3cbe843639c17b1599bf8de89ea74444 Mon Sep 17 00:00:00 2001 From: Pablo Aguilar Date: Sat, 29 Aug 2026 03:44:35 -0300 Subject: [PATCH 09/12] chore: update `requirements.txt` --- Makefile | 4 + requirements-dev.txt | 237 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 237 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 9e4cda4..56f5556 100644 --- a/Makefile +++ b/Makefile @@ -27,3 +27,7 @@ ifeq ($(OS),Windows_NT) else @PYTHONDONTWRITEBYTECODE=1 poetry run pytest endif + +export-requirements-dev: + @poetry self add poetry-plugin-export + @poetry export -f requirements.txt --output requirements-dev.txt --all-groups diff --git a/requirements-dev.txt b/requirements-dev.txt index 9ab950a..b711f3d 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,233 @@ -holidays>=0.58 -num2words==0.5.13 -coverage>=7.2.7 -ruff==0.15.12 +colorama==0.4.6 ; python_version >= "3.10" and python_version < "4.0" and sys_platform == "win32" \ + --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ + --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 +coverage==7.16.0 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:01b18b8a6c9cec8d5f45550e2501426ed982cf2c35016b0acd2ba9b5d8b2fb06 \ + --hash=sha256:0466f4a5c0370461b7d8c7eb259d7d1db0b5756f13d66230b04d22a1d380ee11 \ + --hash=sha256:050a291b3cfe5e0df5999ef2fa5a7aff6e2db329f069d47eb63f02bde2e7e96b \ + --hash=sha256:058631257350b31784ed43ceb808298b6f074edf4ebca4c7ce5082e6bf873a61 \ + --hash=sha256:0598aadae641f30a0796b75b45c0b9c5de8619bd5cfb251bb0cc254e86e6dd13 \ + --hash=sha256:06f20145a9eb5bf1fd1dde3c0bc2af2e7c22135ab07ca6284d6ada7cc3904c4e \ + --hash=sha256:077f0964087883176ff6ab9b074694cae29f8c708273b13ca62c183c6ed716cd \ + --hash=sha256:0bb04ee77e557d7476471969d35fbbfb5fc8a4152e9409aa5811780c36d9b23e \ + --hash=sha256:0ccc37c00e1a5d30840902c54557e104d04aead872cedf6d2281c8725a467e06 \ + --hash=sha256:0fca700cae4635656668ba6e2b66a85aac9f2622d7b2bcf82e844c409eaa1313 \ + --hash=sha256:136988df5bc5a48795d9c42c75c4bbda5d9a78e750a080c1233010edff93a1af \ + --hash=sha256:1420370276f1694b663207b8245c3628aafb9624fe3cebf313a13d860e55ee67 \ + --hash=sha256:151855767480be14db595cbc2040f6a4db965cdfeebd354d79b0256742b029e0 \ + --hash=sha256:1545c52ce756b8a97007f439a220297f1cd72a2cbbcdffccdf1c1f70e74f9a42 \ + --hash=sha256:17fc3628f99812fec24f40092af34c1c73274d331babab3d1d768a75de650cf7 \ + --hash=sha256:181c2906b9b3759955c1c33c51fbb91c754fbd0b82ea49e2c81061f5a052082c \ + --hash=sha256:183613f664718b340589d7f005c7e92b4b601cffd20a8a4117cfda3e983b080f \ + --hash=sha256:190ffa0f5af966254c249fb3aeaca2cef389785e3e287fd577d39e134d20f8a3 \ + --hash=sha256:1a03e78f53e4d2ab13adac19958a89322d1829913e5623d642627bf60b35da21 \ + --hash=sha256:1c2c45ee1853668f0ea1a0ddff396421c9dc5ad25a56bfb94a895970c2d8e7c2 \ + --hash=sha256:1c5a43cc0ef101637ae920a9eed24cf0549ef815621eae68b3ad577ec5a7ad2f \ + --hash=sha256:1f81cb1554c3712e41649ed5dc98656b50b958e4da12f0f5adb681ce3db92831 \ + --hash=sha256:22d8802827404be32f5a4d6ddc037f6fa0074b7d06702c0224cb598def8b665d \ + --hash=sha256:245f7de6d023a5bba375dbec9f2e0869bfa26ac0cc639bbb7b4c814884000b73 \ + --hash=sha256:26e7de0cb87960c6c9b5cad760068dab767b2b49a3b9376e1992c1e2691a015e \ + --hash=sha256:27461af9f3ed7d2cf2411eb083784f87055ebf42211789ae3a216c48609bc743 \ + --hash=sha256:289f2ed4d56eebf029b649e7dfc3c1153b111962a75e294cdd8e4a1598a04cc3 \ + --hash=sha256:2c3ff6580f2dfc5bec34717b85b2e6cf5ec993b721e7bb58a794babd525a8178 \ + --hash=sha256:2ddaa9e2af4760a329d80008b7a3b4762fbb0dbcb169199360f9a5179c32f2dc \ + --hash=sha256:30f5aee6d1d517abcdfd4f9cad027969ff79a1440a22da263f9514e31b5b66e9 \ + --hash=sha256:32c56b5b47c50635081445ac404dd08c2d591b9c837c22570aa9e182c3b42cd4 \ + --hash=sha256:34d8686bce035c8465b318a8c2890e69ba14a00801a27f4eb6bdc97c23944d87 \ + --hash=sha256:35a9676bf86097f790113ebd9fb67681804ef54d40941d2f10ba68c02239e575 \ + --hash=sha256:360967a6fd77794c167529eec2d16ff8e38216110619d23acc3fd466a1648bee \ + --hash=sha256:36aed4951aedf04cbe9465e76f8e71219980a52b73d07afe69746cba6ba7b97a \ + --hash=sha256:38b8e1e73750b8965d1154ed733f5303acd4e24ee2d5ee872bb1bfab744a31ce \ + --hash=sha256:3e8037e8213adf882e9d7eedd2c5c557933ab0b9632c42d98fe98ec9bcdb4025 \ + --hash=sha256:4080ad6bad9f14690e6b2104f5e8d137ccc65a4b5427a36662090637d4bd16d5 \ + --hash=sha256:4212cec9b42fd9929e70b462732fefd8b13406371871c82f3c14397499d6550b \ + --hash=sha256:47d5e1fc0b321c8308a2aacee0497c435b08acaa629b7059798fdf6fc3006352 \ + --hash=sha256:496277c8d7beed695e02c7be53516a0152e4caef8738a0feab6a638546cce449 \ + --hash=sha256:49fa72ead28c8216f8916398a4f3c4669acb30a061822810ee20a727a1be2897 \ + --hash=sha256:4b1d09cb5d8dc2c7164450f5217e6f0717497de9c588806a0780d352abef904a \ + --hash=sha256:4c1f16d5555a195295d0dc9c902612270e3dfed6a11f3bf7bc470b7b6a79ed3c \ + --hash=sha256:4fcb5f07a9b7083bfb715115d27ce263ba2b5b89dddeee536b295ba0e3c2c627 \ + --hash=sha256:507596cee23e9968b1934fe86d799b76166541af0a293930918b1b48a5c84bd2 \ + --hash=sha256:51e7d0e311d2fba3915f971236cbdd4ad821fc7a23988221c0b33c964b0eba22 \ + --hash=sha256:5205baea687133613dced668a3d0168ea1479349615bfc255849a7944988c889 \ + --hash=sha256:54b7fba6a74d010de34319a0419d5b65af8c00f539ad0b6f39fc6f342ab99697 \ + --hash=sha256:55957d350452017f523b9b03ffac078f9a214e23c04a3d0a674569203550c719 \ + --hash=sha256:577c2ac8c0036f6f8edd3a7783a9e67302b17771d1abf0fd2ed246e3158be51b \ + --hash=sha256:584896fb8b650e999e24ef57e9513e482c12f8e15a73ee9d4584e23c99465867 \ + --hash=sha256:5dad64d9c17cb1983adef07998e6e2e1cf870a156f1ea80f81ce1970f4c545ce \ + --hash=sha256:64f0611ee05364fc85cc3e5bc371804117a76fd337720e6017332fc7c534257a \ + --hash=sha256:69474d81f198774c9d2937599ca5da04c9e1c5de5032da23c607ce4960ce360e \ + --hash=sha256:6ad3bbad240ab937512156bc944fdee63ac4dd34a7558a3094548fd4c1150c02 \ + --hash=sha256:6c60cde430c0e7e3be612973af39b4cff90ec2e2defe7b2b701daea3a0ffff04 \ + --hash=sha256:6e2854b62601c89a63814ad5def3b90d99c6724cc4cb977f75b725e5fca4b1e3 \ + --hash=sha256:6e701938ec9081d3e400a0c9a9a8ae0f7ca44214741daeac4454b1c6ef6dbd19 \ + --hash=sha256:6fde65e0ea945920265dfe4a2108fc45eee2e2ea3d9c3073af6373ff9836aa71 \ + --hash=sha256:719a3feb6220dd32ed932d4c3676d17fb8739e2643b29c0e7c3af400ff80ac44 \ + --hash=sha256:72a0795cc6d34acc2b03dfeabdc82b61b72087f2737018b56ac92c1cf5446c54 \ + --hash=sha256:770d4244c423dcafb5c31db393f429fe952b1bba23bbff7cc3886f8133769ba5 \ + --hash=sha256:78103e79f9378cb0e43ddaa728629a373c070df903c5dfa98b63ba2cfb4e8c42 \ + --hash=sha256:785b114356c99c0dd5b3f57b9696cfd57b7704f4c53847df8dc88c6cc0d9bcb6 \ + --hash=sha256:78f8b56261d608be102c62edd3a60b66bcd0b581f3f86fdcabaf8b8d95adc950 \ + --hash=sha256:7cae7715afa51dd7c9c42e6603bb46daf424c3449fdf06519cc658aa8d46e2e4 \ + --hash=sha256:80cf547379ad6b1878fd03b033b51188beab4b41824c96e7839e014a4cb947be \ + --hash=sha256:80d7d5d744a041f08637df743ac086204ec5acbcd8432a42b00b49e607358024 \ + --hash=sha256:81d63b68b26304e3668edb103311c17fe13c2ed1c7fe973309819f27bf61c5b8 \ + --hash=sha256:857fceba6ff4b507ee0ad98798a33d544a8473df0c542bf04251ee4ed5ee6292 \ + --hash=sha256:87771ecf986cff55e87413238cd5e4f54d949c2074bd6fc1657d26a56314ee24 \ + --hash=sha256:916cf8d25c1ce148f7eceb1d45afc9724841200110adc4e53250391852debd91 \ + --hash=sha256:92cbc2bf4f7f67c79f1d3ca4fe8c50faddf48e852a3d07eaaf02dc014889832f \ + --hash=sha256:9421dde689e68d9fd2b6cd7d8c4498e79b5431467b6298517e3f3e60fdbe80a7 \ + --hash=sha256:949eae7e0f562b1518355aaef4b03523e49a6d3fea12aa3542d9e36c863f8267 \ + --hash=sha256:97051c4903689b1afedc2a354d6118223051e03588078b53048603bda9014577 \ + --hash=sha256:984e5430fc6f858385009e92549955157d79335b1f3e13e1031e0f89d1284261 \ + --hash=sha256:9b83f6ac575530783771c8dcf05284f7c8b5b12f1e7cb226d63445aac4497a3a \ + --hash=sha256:9c0690994b84a15a53bdd39e0b2fdb539b22533820623eb86ba75b93760c645b \ + --hash=sha256:a336b1e2990a64f5c356a9b8380fb9c029d56c832b801255250c44d603271bfd \ + --hash=sha256:a3cd34b9025d62180ce2b5dae8a985bfa6cb8c05ecd57fd34ffc1ff751b5a74d \ + --hash=sha256:a739bf08cdca0fad51b73322e4fade0102dd87794e278450b5ee87ef827954db \ + --hash=sha256:a89d07e48d9baead9a15599923a02f62c6df6c3d85aa84ef34be3c9fd6aeb91f \ + --hash=sha256:acadbf2f2a18d7f9c7f119ac798c00c540d7c79c93abd71ed648c87891303633 \ + --hash=sha256:b1374099dd1ad0d31fbb6c95d00a56a3c5e85fb3343dca14fc12f78323a2b42a \ + --hash=sha256:b2af58ecdcec37fe633d4865fccbc8c00d8aa3b31c099bcacb2720c9a0be6ab9 \ + --hash=sha256:b37ad5cbb77776f446e1b55b461eec2eef5c3e7130c72dc0e1447c3a9da2d199 \ + --hash=sha256:b670bd5fa93d9b6855b2837217b45a90863118e2de5e9e033aebd46d07cd08d3 \ + --hash=sha256:b7dbbbf6551eb94618e7bc76ab61cc2740a5b3d13294171bd6adb36e12346c3c \ + --hash=sha256:bbf08d951abaa1ce89e28c998361d56b952413846b459cd017f116ad4c9adbfa \ + --hash=sha256:c1bcfe470a796fbea6234accd81d258a31574dc0b7bf569e16be757572c4de17 \ + --hash=sha256:c5297028c8df849a61b29129cadfe682f90b5b396f528eb319a57d7678eefdad \ + --hash=sha256:c5612cc20ca76abc883e50269af47c1494b42958bb63dbb9aa79729a1ab5f7d3 \ + --hash=sha256:c5feffce90c3d602e149de1c477578efc34dee5f069f9764cc15808ce01ee15c \ + --hash=sha256:c72c9b201dc0e8c2c8821d49858fd865010d08181bf877d2320971b6464ebfd5 \ + --hash=sha256:c76a9b50a344261fe4a9bd20c322b48d3913cc48e8c37f78c21a596008296e68 \ + --hash=sha256:c94ef980f7b94d9dab9dac076d44ca706654cd51bad19734e029084adf528c8e \ + --hash=sha256:cb953835dbfa6d641ac3943e0986bc680f8abbdc2985af15b46c54985347146a \ + --hash=sha256:cc12e5e32acdd62fe5895939695579560639853219288519685c75b7e968d63a \ + --hash=sha256:cce4dc8528453128c6fae523b15f3887fbea1d4d7c9eb9639d3d4fdcbe570c73 \ + --hash=sha256:cd1e85abed2d2499c16664137ac802356316f92b4e2bf3c150bdf0c45f5dd9ae \ + --hash=sha256:ce2ba5e9f1842fe09165825abfb3bc6b527c71a27bc2eb3a10f2284ced64506d \ + --hash=sha256:d1c77c3579ac42798f8b7eed6d3dd258debacca32c8753fc8a1f6eaf1db644f5 \ + --hash=sha256:d568a8adcec0eda42ec23e5e65dfb8c184fc255120f9e99b484f7c869d923fb9 \ + --hash=sha256:d9a218d3f9c7d6916684ed5ba94f620661117a730e733cd6ef5e87accc5872eb \ + --hash=sha256:dcd3dafcdd78305d27c59a1006b53a4990acb89e68d8fbe0992f4f83503c827f \ + --hash=sha256:de24c62bf798940a14674a47489a81b79915ec4134f556d5199830e065225dd0 \ + --hash=sha256:e40e323711b485592354069b1c027ef879cc2d11657eac09a6e5ad0b49ab7406 \ + --hash=sha256:e6b2b9599e7513b0a9c5bf0357f9f8deaa4c2c821025b0693d420e6602748981 \ + --hash=sha256:e9883a2f8206ce3af59117dc278e5d043fea06912bca3f199816129e5e2de354 \ + --hash=sha256:ebaf39dd13f8af65fe5f0316b81046228ef4d91d3c3766192b418753649896d6 \ + --hash=sha256:ed35097438dfa980c1ec75bc83edf8acbe7a374d7007e571957a257fbd0e2fb3 \ + --hash=sha256:edc2be98e6c55ccc5ff7832bb64f023a4b03dba39dfa84b850046cf08a8249b0 \ + --hash=sha256:f093faf23df888518d273be6da65f0ec5a25b5d8b670231e4d87de07361042e7 \ + --hash=sha256:f6c9c21a8bf0d19788f3c5f3e020c90317a0a63ef60521b376003801e21250fb \ + --hash=sha256:f98d438add63546745e5e847192e3e9ab897ed6f2ca96f8281e2f5a15958ae62 \ + --hash=sha256:f99d12f8234c00b88b8077fedf288b25c77f746de312053b7db90fa756ecbdb3 \ + --hash=sha256:fa4ff0b3dd52208d2b30903022d5087f82000507b504753dfeee83e4f32d6883 \ + --hash=sha256:fddd26ed9a2527a7e23f7e4c1fd0734c4a5b45f77b261da1c536b20a7d2e6f0c \ + --hash=sha256:fe5aa402d02318db2f41e471320b2ecca6085b8f595a034c037085732e49c04a +docopt==0.6.2 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491 +exceptiongroup==1.3.1 ; python_version == "3.10" \ + --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \ + --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598 +holidays==0.101 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:be1d5bb0b662d709da08dcdfa9b42feb678312df2a5d6280421da71349e4757f \ + --hash=sha256:fbaa802d66e5d24877359ae68c46d00a73437162d3e9a17466fe6cfcce123c3a +iniconfig==2.3.0 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \ + --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 +num2words==0.5.14 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:1c8e5b00142fc2966fd8d685001e36c4a9911e070d1b120e1beb721fa1edb33d \ + --hash=sha256:b066ec18e56b6616a3b38086b5747daafbaa8868b226a36127e0451c0cf379c6 +packaging==26.3 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79 \ + --hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c +pluggy==1.6.0 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ + --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 +pygments==2.21.0 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9 \ + --hash=sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c +pytest-cov==7.1.0 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2 \ + --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678 +pytest-randomly==4.1.0 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:47f1d9746c3bc3efabd53ae1ebfb8bb385cf3d4df4b505b6d58d9c97a3dfe70f \ + --hash=sha256:f55e89e53367b090c0c053697d7f9d77595543d0e0516c93978b50c0f6b252f9 +pytest==9.1.1 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313 \ + --hash=sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c +python-dateutil==2.9.0.post0 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ + --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 +ruff==0.15.17 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:0e01a84ddbc8c16c23055ba3924476850f1bbc1917cebbb9376665a63e74260d \ + --hash=sha256:25805a226d741c47d274a35ad5c10a7dde175fcddfa511d7cf3da0a21eb3eab7 \ + --hash=sha256:2ec446937fd16c8c4de2674a209cc5af64d9c6f17d21fbf1151054fa0bcf5219 \ + --hash=sha256:382fc0521025f5a8ad447d8bdd523545d0d7646adb718eb1c2dac5065ec27c0f \ + --hash=sha256:456d41fcd1b2777ad63f09a6e7121d43f7b688bbc76a800c10f7f8fb1f912c3f \ + --hash=sha256:596065960ab1ff593f744220c9fe6580eda00a95003cffa9f4048bb5b1bf0392 \ + --hash=sha256:6769e5fa1710b179b92e0bfa5a51735b35baea9013dadb06d5f44cbcf9547084 \ + --hash=sha256:6ba0c1e4f95bcb3869d0d30cbd5917071ef2e28665abfec970cdab0492c713ed \ + --hash=sha256:6eccbe50a038b503e7140b441aa9c7fc8c1f36edf23ebef9f4165c2f28f568b7 \ + --hash=sha256:81647960f10bff57d2e51cadd0c3950fe598400c852863a038720ef5b8cca91e \ + --hash=sha256:84fe9f653152f8f294f9f7e03bf3a453d8b4a27f7a59c78c8666167f2b17b96c \ + --hash=sha256:8c0fe88a7676e7a05b73174d4d4a59cb2ac21ff8263583f87a81a6018475a978 \ + --hash=sha256:b1a04bcc94ae6194e9db05d16ad31f298a7194bfbcb08258bbe589cee1d587b8 \ + --hash=sha256:b8461180b22420b1bdc289909410930761629fddf2a5aaf60fae1ab26cedc4c4 \ + --hash=sha256:d9feddb927fc68bd295f5eebc587a7e42cfaf9b65f60ca4a2386febff575da8f \ + --hash=sha256:ecfc3c7878fff94633ab0348524e093f9ce3243080416dd7d14f8ba400174719 \ + --hash=sha256:f3be1fbb34bcdfd146240d8fb92a709d4c2c8191348580a3c044ec60fa0b4456 \ + --hash=sha256:f6ad73b14c2d18a3bf8ad7cb6974294d7f613a7898604826058e6ac64918ef4d +six==1.17.0 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ + --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 +tomli==2.4.1 ; python_version >= "3.10" and python_full_version <= "3.11.0a6" \ + --hash=sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853 \ + --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \ + --hash=sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5 \ + --hash=sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d \ + --hash=sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd \ + --hash=sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26 \ + --hash=sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54 \ + --hash=sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6 \ + --hash=sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c \ + --hash=sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a \ + --hash=sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd \ + --hash=sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f \ + --hash=sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5 \ + --hash=sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9 \ + --hash=sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662 \ + --hash=sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 \ + --hash=sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1 \ + --hash=sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585 \ + --hash=sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e \ + --hash=sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c \ + --hash=sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41 \ + --hash=sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f \ + --hash=sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085 \ + --hash=sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15 \ + --hash=sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7 \ + --hash=sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c \ + --hash=sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36 \ + --hash=sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076 \ + --hash=sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac \ + --hash=sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8 \ + --hash=sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232 \ + --hash=sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece \ + --hash=sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a \ + --hash=sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897 \ + --hash=sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d \ + --hash=sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4 \ + --hash=sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917 \ + --hash=sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396 \ + --hash=sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a \ + --hash=sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc \ + --hash=sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba \ + --hash=sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f \ + --hash=sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257 \ + --hash=sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 \ + --hash=sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf \ + --hash=sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9 \ + --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049 +typing-extensions==4.16.0 ; python_version == "3.10" \ + --hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8 \ + --hash=sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5 From 3f3fca25c2c815dbd5e2a2bdb2648d7bb5b4c6ab Mon Sep 17 00:00:00 2001 From: Pablo Aguilar Date: Sat, 29 Aug 2026 11:33:05 -0300 Subject: [PATCH 10/12] tests: add more tests --- brutils/voter_id.py | 2 +- tests/test_cnh.py | 3 +++ tests/test_renavam.py | 1 + tests/test_voter_id.py | 11 +++++++++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/brutils/voter_id.py b/brutils/voter_id.py index 7a1abd9..641e40c 100644 --- a/brutils/voter_id.py +++ b/brutils/voter_id.py @@ -256,7 +256,7 @@ def generate(federative_union: str = "ZZ") -> str | None: } federative_union = federative_union.upper() - if federative_union in (UFs): + if federative_union in UFs: sequential_number = str(randint(0, 99999999)).zfill(8) uf_number = UFs[federative_union] if _is_federative_union_valid(uf_number): diff --git a/tests/test_cnh.py b/tests/test_cnh.py index a1049be..7998e2e 100644 --- a/tests/test_cnh.py +++ b/tests/test_cnh.py @@ -8,5 +8,8 @@ def test_is_valid_cnh(self): self.assertFalse(is_valid_cnh("22222222222")) self.assertFalse(is_valid_cnh("ABC70304734")) self.assertFalse(is_valid_cnh("6619558737912")) + self.assertFalse(is_valid_cnh("AABBCCDDEE")) + self.assertFalse(is_valid_cnh("09770304714")) + self.assertFalse(is_valid_cnh("09770304731")) self.assertTrue(is_valid_cnh("097703047-34")) self.assertTrue(is_valid_cnh("09770304734")) diff --git a/tests/test_renavam.py b/tests/test_renavam.py index 8ff900e..402043b 100644 --- a/tests/test_renavam.py +++ b/tests/test_renavam.py @@ -16,3 +16,4 @@ def test_is_valid_renavam(self): self.assertFalse(is_valid_renavam("12345678901!")) self.assertFalse(is_valid_renavam("00000000000")) self.assertFalse(is_valid_renavam("11111111111")) + self.assertFalse(is_valid_renavam(11111111111)) diff --git a/tests/test_voter_id.py b/tests/test_voter_id.py index 738ec77..73fe273 100644 --- a/tests/test_voter_id.py +++ b/tests/test_voter_id.py @@ -128,5 +128,16 @@ def test_format_voter_id(self): self.assertIsNone(format_voter_id("800911840197")) +class TestGenerate(TestCase): + def test_generate_valid(self): + voter_id = generate() + self.assertIsNotNone(voter_id) + self.assertTrue(is_valid(voter_id)) + + def test_generate_invalid(self): + voter_id = generate(federative_union="XX") + self.assertIsNone(voter_id) + + if __name__ == "__main__": main() From d9e3601e8303ac3900e8fc7b7894721a32cbf6f7 Mon Sep 17 00:00:00 2001 From: Pablo Aguilar Date: Sat, 29 Aug 2026 11:38:33 -0300 Subject: [PATCH 11/12] chore: add readmes in the test paths --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 34aaa09..7460494 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,6 +67,8 @@ addopts = [ testpaths = [ "brutils", "tests", + "README.md", + "README_EN.md", ] [build-system] From 198097bc0d94f8d51438eb22fe2082743d631de5 Mon Sep 17 00:00:00 2001 From: Pablo Aguilar Date: Sat, 29 Aug 2026 11:57:20 -0300 Subject: [PATCH 12/12] fix: fix test mock --- tests/test_cep.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_cep.py b/tests/test_cep.py index e47b12d..d629416 100644 --- a/tests/test_cep.py +++ b/tests/test_cep.py @@ -89,8 +89,8 @@ def test_get_address_from_cep_invalid_cep_raise_exception_cep_not_found( self, mock_urlopen ): mock_data = MagicMock() - mock_data.read.return_value = {"erro": True} - mock_urlopen.return_value = mock_data + mock_data.read.return_value = '{"erro": true}' + mock_urlopen.return_value.__enter__.return_value = mock_data with self.assertRaises(CEPNotFound): get_address_from_cep("01310209", True)