├── .github └── workflows │ └── unittests.yml ├── .gitignore ├── .idea ├── .gitignore ├── acbrlib-python.iml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── LICENSE ├── README.rst ├── acbrlib_python ├── __init__.py ├── cep │ ├── __init__.py │ ├── constantes.py │ ├── excecoes.py │ ├── impl.py │ └── modelos.py ├── constantes.py ├── excecoes.py ├── mixins.py └── proto.py ├── exemplos └── cep.py ├── poetry.lock ├── pyproject.toml └── tests ├── __init__.py ├── cep ├── __init__.py └── test_resposta.py ├── test_acbrlib_python.py └── test_proto.py /.github/workflows/unittests.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests Runner 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | unittests: 7 | name: Unit Tests Runner on Python ${{ matrix.python-version }} 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | python-version: ['3.9'] 12 | poetry-version: ['1.1.10'] 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | 17 | - name: Setup Python ${{ matrix.python-version }} 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: ${{ matrix.python-version }} 21 | architecture: 'x64' 22 | 23 | - name: Setup Python Poetry ${{ matrix.poetry-version }} 24 | uses: abatilo/actions-poetry@v2.1.3 25 | with: 26 | poetry-version: ${{ matrix.poetry-version }} 27 | 28 | - name: Run Tests 29 | run: | 30 | poetry install 31 | poetry run pytest 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # Installer logs 30 | pip-log.txt 31 | pip-delete-this-directory.txt 32 | 33 | # Unit test / coverage reports 34 | htmlcov/ 35 | .tox/ 36 | .nox/ 37 | .coverage 38 | .coverage.* 39 | .cache 40 | nosetests.xml 41 | coverage.xml 42 | *.cover 43 | *.py,cover 44 | .hypothesis/ 45 | .pytest_cache/ 46 | cover/ 47 | 48 | # Sphinx documentation 49 | docs/_build/ 50 | 51 | # Jupyter Notebook 52 | .ipynb_checkpoints 53 | 54 | # IPython 55 | profile_default/ 56 | ipython_config.py 57 | 58 | # pyenv 59 | .python-version 60 | 61 | # Environments 62 | .env 63 | .venv 64 | env/ 65 | venv/ 66 | ENV/ 67 | env.bak/ 68 | venv.bak/ 69 | 70 | # mypy 71 | .mypy_cache/ 72 | .dmypy.json 73 | dmypy.json 74 | 75 | # Pyre type checker 76 | .pyre/ 77 | 78 | # pytype static type analyzer 79 | .pytype/ 80 | 81 | # Cython debug symbols 82 | cython_debug/ 83 | 84 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 85 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 86 | 87 | # User-specific stuff 88 | .idea/**/workspace.xml 89 | .idea/**/tasks.xml 90 | .idea/**/usage.statistics.xml 91 | .idea/**/dictionaries 92 | .idea/**/shelf 93 | 94 | # AWS User-specific 95 | .idea/**/aws.xml 96 | 97 | # Generated files 98 | .idea/**/contentModel.xml 99 | 100 | # Sensitive or high-churn files 101 | .idea/**/dataSources/ 102 | .idea/**/dataSources.ids 103 | .idea/**/dataSources.local.xml 104 | .idea/**/sqlDataSources.xml 105 | .idea/**/dynamic.xml 106 | .idea/**/uiDesigner.xml 107 | .idea/**/dbnavigator.xml 108 | 109 | # Gradle 110 | .idea/**/gradle.xml 111 | .idea/**/libraries 112 | 113 | # Gradle and Maven with auto-import 114 | # When using Gradle or Maven with auto-import, you should exclude module files, 115 | # since they will be recreated, and may cause churn. Uncomment if using 116 | # auto-import. 117 | # .idea/artifacts 118 | # .idea/compiler.xml 119 | # .idea/jarRepositories.xml 120 | # .idea/modules.xml 121 | # .idea/*.iml 122 | # .idea/modules 123 | # *.iml 124 | # *.ipr 125 | 126 | # CMake 127 | cmake-build-*/ 128 | 129 | # Mongo Explorer plugin 130 | .idea/**/mongoSettings.xml 131 | 132 | # File-based project format 133 | *.iws 134 | 135 | # IntelliJ 136 | out/ 137 | 138 | # mpeltonen/sbt-idea plugin 139 | .idea_modules/ 140 | 141 | # JIRA plugin 142 | atlassian-ide-plugin.xml 143 | 144 | # Cursive Clojure plugin 145 | .idea/replstate.xml 146 | 147 | # Crashlytics plugin (for Android Studio and IntelliJ) 148 | com_crashlytics_export_strings.xml 149 | crashlytics.properties 150 | crashlytics-build.properties 151 | fabric.properties 152 | 153 | # Editor-based Rest Client 154 | .idea/httpRequests 155 | 156 | # Android studio 3.1+ serialized cache file 157 | .idea/caches/build_file_checksums.ser 158 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/acbrlib-python.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | 2 | ============== 3 | ACBrLib Python 4 | ============== 5 | 6 | |PyPI pyversions| |PyPI version fury.io| |PyPI license| 7 | 8 | Camada de abstração para acesso à `ACBrLib`_ em Python. 9 | 10 | ---- 11 | 12 | `ACBrLib`_ é um conjunto de bibliotecas voltadas para o mercado de 13 | automação comercial que oferece acesso à um conjunto rico de abstrações 14 | que facilitam o desenvolvimento de aplicações como pontos-de-venda (PDV) e 15 | aplicações relacionadas. Esta biblioteca fornece uma camada que torna 16 | trivial a utilização da `ACBrLib`_ em seus próprios aplicativos escritos em 17 | `linguagem Python `_. 18 | 19 | .. note:: 20 | 21 | Esta biblioteca está em seus primeiros estágios de desenvolvimento, 22 | portanto, não espere encontrar toda a riqueza que os componentes 23 | `ACBr`_ possuem, por enquanto. Mas estamos totalmente abertos a 24 | `sujestões`_ e estamos aceitando `pull-requests`_. 25 | 26 | 27 | Instalação 28 | ---------- 29 | 30 | Instale, preferencialmente em um ambiente virtual, usando ``pip``: 31 | 32 | .. code-block:: shell 33 | 34 | pip install acbrlib-python 35 | 36 | 37 | ACBrLibCEP 38 | ---------- 39 | 40 | Dá acesso a consultas de CEP utilizando dezenas de serviços de consulta 41 | disponíveis. Alguns desses serviços podem ser gratuítos ou gratuítos para 42 | desenvolvimento. Veja `este link `_ 43 | para ter uma ideia dos serviços que podem ser utilizados. 44 | 45 | Para fazer uma consulta baseada no CEP: 46 | 47 | .. code-block:: python 48 | 49 | from acbrlib_python import ACBrLibCEP 50 | 51 | with ACBrLibCEP.usando('/caminho/para/libacbrcep64.so') as cep: 52 | enderecos = cep.buscar_por_cep('18270170') 53 | for endereco in enderecos: 54 | print(endereco) 55 | 56 | O trecho acima resultará em uma lista de objetos ``Endereco`` como resultado 57 | da busca, prontos para serem usados. A consulta acima trará um único resultado 58 | (usando o serviço `ViaCEP `_): 59 | 60 | .. code-block:: python 61 | 62 | Endereco( 63 | tipo_logradouro='', 64 | logradouro='Rua Coronel Aureliano de Camargo', 65 | complemento='', 66 | bairro='Centro', 67 | municipio='Tatuí', 68 | uf='SP', 69 | cep='18270-170', 70 | ibge_municipio='3554003', 71 | ibge_uf='35' 72 | ) 73 | 74 | Para mais exemplos de uso, veja a pasta ``exemplos`` neste repositório. 75 | 76 | 77 | Sobre Nomenclatura e Estilo de Código 78 | ===================================== 79 | 80 | Uma questão muito relevante é a maneira como esta abstração se refere aos 81 | nomes dos métodos disponíveis na biblioteca `ACBrLib`_ que utiliza uma 82 | convenção de nomenclatura para variáveis e nomes de argumentos ou 83 | parâmetros de funções conhecida como `Notação Húngara `_. 84 | Porém, em Python é utilizada a convenção `snake case `_ 85 | tal como descrito na `PEP8 `_. 86 | 87 | Assim, para manter o estilo de Python, os nomes de variáveis e argumentos de 88 | função deverão descartar o prefixo que indica o tipo de dado e converter o 89 | restante para snake case, assim como nomes de métodos e funções também, 90 | por exemplo: 91 | 92 | * `eArqConfig` para `arq_config`; 93 | * `ConfigLerValor` para `config_ler_valor`; 94 | * `eArquivoXmlEvento` para `arquivo_xml_evento`; 95 | * etc… 96 | 97 | Métodos de bibliotecas que são prefixados com o nome da lib, será descartado o 98 | prefixo e o restante do nome do método convertido para snake case, por exemplo: 99 | 100 | * (ACBrLibNFe) `NFE_CarregarINI` para `carregar_ini`; 101 | * (ACBrLibNFe) `NFE_ValidarRegrasdeNegocios` para `validar_regras_de_negocios` 102 | (note a correção do conector `de` que está em minúsculo no original); 103 | * (ACBrLibCEP) `CEP_BuscarPorLogradouro` para `buscar_por_logradouro`; 104 | * etc… 105 | 106 | Esperamos que essa explicação faça sentido, senão, envia suas `sujestões`_. 107 | 108 | 109 | Desenvolvimento 110 | =============== 111 | 112 | Você é bem-vindo para ajudar no desenvolvimento desta biblioteca enviando 113 | suas contribuições através de `pull-requests`_. Faça um *fork* deste 114 | repositório e execute os testes antes de começar a implementar alguma 115 | coisa. A gestão de dependências é feita via `Poetry`_ e recomendamos a 116 | utilização de `pyenv`_ 117 | 118 | .. code-block:: shell 119 | 120 | $ git clone https://github.com/base4sistemas/acbrlib-python.git 121 | $ cd acbrlib-python 122 | $ poetry install 123 | $ poetry run pytest 124 | 125 | 126 | .. _`sujestões`: https://github.com/base4sistemas/acbrlib-python/issues 127 | .. _`pull-requests`: https://github.com/base4sistemas/acbrlib-python/pulls 128 | .. _`ACBr`: https://projetoacbr.com.br/ 129 | .. _`ACBrLib`: https://projetoacbr.com.br/downloads/#acbrlib 130 | .. _`pyenv`: https://github.com/pyenv/pyenv 131 | .. _`Poetry`: https://python-poetry.org/ 132 | 133 | .. |PyPI pyversions| image:: https://img.shields.io/pypi/pyversions/acbrlib-python.svg 134 | :target: https://pypi.python.org/pypi/acbrlib-python/ 135 | 136 | .. |PyPI version fury.io| image:: https://badge.fury.io/py/acbrlib-python.svg 137 | :target: https://pypi.python.org/pypi/acbrlib-python/ 138 | 139 | .. |PyPI license| image:: https://img.shields.io/pypi/l/acbrlib-python.svg 140 | :target: https://pypi.python.org/pypi/acbrlib-python/ 141 | -------------------------------------------------------------------------------- /acbrlib_python/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # acbrlib_python/__init__.py 4 | # 5 | # Copyright 2021 Base4 Sistemas 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | from .cep import * # noqa: 21 | 22 | __version__ = '0.1.0' 23 | -------------------------------------------------------------------------------- /acbrlib_python/cep/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # acbrlib_python/cep/__init__.py 4 | # 5 | # Copyright 2021 Base4 Sistemas 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | from .impl import ACBrLibCEP # noqa: 21 | 22 | __all__ = ['ACBrLibCEP'] 23 | -------------------------------------------------------------------------------- /acbrlib_python/cep/constantes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # acbrlib_python/cep/constantes.py 4 | # 5 | # Copyright 2021 Base4 Sistemas 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | CEP_WS_NENHUM = 0 21 | CEP_WS_BUSCARCEP = 1 22 | CEP_WS_CEPLIVRE = 2 23 | CEP_WS_REPUBLICAVIRTUAL = 3 24 | CEP_WS_BASES4YOU = 4 25 | CEP_WS_RNSOLUCOES = 5 26 | CEP_WS_KINGHOST = 6 27 | CEP_WS_BYJG = 7 28 | CEP_WS_CORREIOS = 8 29 | CEP_WS_DEVMEDIA = 9 30 | CEP_WS_VIACEP = 10 31 | CEP_WS_CORREIOSSIGEP = 11 32 | CEP_WS_CEPABERTO = 12 33 | CEP_WS_WSCEP = 13 34 | 35 | CEP_WEBSERVICES = ( 36 | (CEP_WS_NENHUM, 'Nenhum'), 37 | (CEP_WS_BUSCARCEP, 'BuscarCEP'), 38 | (CEP_WS_CEPLIVRE, 'CEP Livre'), 39 | (CEP_WS_REPUBLICAVIRTUAL, 'República Virtual'), 40 | (CEP_WS_BASES4YOU, 'Bases4You'), 41 | (CEP_WS_RNSOLUCOES, 'RN Soluções'), 42 | (CEP_WS_KINGHOST, 'King Host'), 43 | (CEP_WS_BYJG, 'By JG'), 44 | (CEP_WS_CORREIOS, 'Correios'), 45 | (CEP_WS_DEVMEDIA, 'DevMedia'), 46 | (CEP_WS_VIACEP, 'ViaCEP'), 47 | (CEP_WS_CORREIOSSIGEP, 'Correios SIGEP'), 48 | (CEP_WS_CEPABERTO, 'CEP Aberto'), 49 | (CEP_WS_WSCEP, 'WS CEP'), 50 | ) 51 | -------------------------------------------------------------------------------- /acbrlib_python/cep/excecoes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # acbrlib_python/cep/excecoes.py 4 | # 5 | # Copyright 2021 Base4 Sistemas 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | from ..excecoes import ACBrLibException 21 | 22 | 23 | class ACBrLibCEPException(ACBrLibException): 24 | pass 25 | 26 | 27 | class ACBrLibCEPErro(Exception): 28 | pass 29 | 30 | 31 | class ACBrLibCEPErroResposta(ACBrLibCEPErro): 32 | pass 33 | -------------------------------------------------------------------------------- /acbrlib_python/cep/impl.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # acbrlib_python/cep/impl.py 4 | # 5 | # Copyright 2021 Base4 Sistemas 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | import configparser 21 | import io 22 | 23 | from contextlib import contextmanager 24 | from ctypes import POINTER 25 | from ctypes import c_char_p 26 | from ctypes import c_int 27 | from typing import List 28 | from typing import Mapping 29 | from typing import Type 30 | 31 | from ..constantes import AUTO 32 | from ..excecoes import ACBrLibException 33 | from ..mixins import ACBrLibCommonMixin 34 | from ..mixins import ACBrLibConfigMixin 35 | from ..proto import ACBrLibReferencia 36 | from ..proto import ReferenceLibrary 37 | from ..proto import Signature 38 | from ..proto import common_method_prototypes 39 | from ..proto import config_method_prototypes 40 | from ..proto import read_string_buffer 41 | 42 | from .excecoes import ACBrLibCEPException 43 | from .excecoes import ACBrLibCEPErroResposta 44 | from .modelos import Endereco 45 | 46 | 47 | class ACBrLibCEP(ACBrLibReferencia, ACBrLibCommonMixin, ACBrLibConfigMixin): 48 | 49 | def __init__( 50 | self, 51 | prefixo: str, 52 | biblioteca: ReferenceLibrary, 53 | prototipos: Mapping[str, Signature], 54 | base_exception: Type[ACBrLibException]): 55 | super().__init__(prefixo, biblioteca, prototipos, base_exception) 56 | 57 | @staticmethod 58 | def usar(caminho_biblioteca, convencao_chamada=AUTO): 59 | prototypes = { 60 | **common_method_prototypes('CEP'), 61 | **config_method_prototypes('CEP'), 62 | 'CEP_BuscarPorCEP': Signature([c_char_p, c_char_p, POINTER(c_int)]), # eCEP, sResposta, esTamanho, 63 | 'CEP_BuscarPorLogradouro': Signature([ 64 | c_char_p, # eCidade 65 | c_char_p, # eTipo_Logradouro 66 | c_char_p, # eLogradouro 67 | c_char_p, # eUF 68 | c_char_p, # eBairro 69 | c_char_p, # sResposta 70 | POINTER(c_int), # esTamanho 71 | ]) 72 | } 73 | instancia = ACBrLibCEP( 74 | 'CEP', 75 | ReferenceLibrary( 76 | caminho_biblioteca, 77 | calling_convention=convencao_chamada 78 | ), 79 | prototypes, 80 | ACBrLibCEPException 81 | ) 82 | return instancia 83 | 84 | @classmethod 85 | @contextmanager 86 | def usando( 87 | cls, 88 | caminho_biblioteca, 89 | convencao_chamada=AUTO, 90 | arq_config='', 91 | chave_crypt=''): 92 | cep = cls.usar(caminho_biblioteca, convencao_chamada=convencao_chamada) 93 | cep.inicializar(arq_config, chave_crypt) 94 | try: 95 | yield cep 96 | finally: 97 | cep.finalizar() 98 | 99 | def buscar_por_cep(self, numero: str) -> List[Endereco]: 100 | """ 101 | Faz uma busca pelo número do CEP. 102 | :param numero: Número do CEP. Deve possuir exatamente oito digitos e 103 | pode ou não estar formatado (qualquer caracter que não seja um 104 | digito, será ignorado). 105 | :return: Retorna uma lista de :class:`~acbrlib_python.cep.Endereco`. 106 | :raise ValueError: Se o argumento não possuir oito digitos, após 107 | todos os caracteres não-digito terem sido removidos. 108 | """ 109 | cep = ''.join([c for c in numero if c.isdigit()]) 110 | if len(cep) != 8: 111 | raise ValueError( 112 | f'CEP informado nao possui nove digitos: {numero!r}' 113 | ) 114 | metodo = f'{self._prefixo}_BuscarPorCEP' 115 | resposta = read_string_buffer(self, metodo, self._b(cep)) 116 | return processar_resposta(resposta) 117 | 118 | def buscar_por_logradouro( 119 | self, 120 | tipo_logradouro='', 121 | logradouro='', 122 | bairro='', 123 | municipio='', 124 | uf='') -> List[Endereco]: 125 | """ 126 | Faz uma busca por determinados atributos de um endereço. Embora, 127 | todos os parâmetros sejam opcionais, para que você obtenha um 128 | resultado mais próximo do que procura, especifique o maior número de 129 | atributos que conheçer do endereço. 130 | 131 | A precisão do resultado irá depender da qualidade dos valores dos 132 | atributos informados e do serviço de busca de CEP que estiver usando. 133 | 134 | :param str tipo_logradouro: Opcional. O tipo do logradouro (rua, 135 | avenida, etc). 136 | :param str logradouro: Opcional. O nome do logradouro. 137 | :param str bairro: Opcional. O nome do bairro. 138 | :param str municipio: Opcional. O nome do município. 139 | :param str uf: Opcional. A sigla do Estado do município. 140 | 141 | :return: Retorna uma lista de :class:`~acbrlib_python.cep.Endereco`. 142 | """ 143 | metodo = f'{self._prefixo}_BuscarPorLogradouro' 144 | resposta = read_string_buffer( 145 | self, 146 | metodo, 147 | self._b(municipio), 148 | self._b(tipo_logradouro), 149 | self._b(logradouro), 150 | self._b(uf), 151 | self._b(bairro), 152 | ) 153 | return processar_resposta(resposta) 154 | 155 | 156 | def processar_resposta(resposta: str) -> List[Endereco]: 157 | buf = io.StringIO(resposta) 158 | parser = configparser.ConfigParser() 159 | parser.read_file(buf) 160 | section, option = 'CEP', 'Quantidade' 161 | if not parser.has_option(section, option): 162 | raise ACBrLibCEPErroResposta( 163 | 'Resposta mal formada; a resposta nao possui a ' 164 | 'informacao da quantidade de enderecos encontrados; ' 165 | f'resposta={resposta!r}' 166 | ) 167 | enderecos = [] 168 | for i in range(parser.getint(section, option)): 169 | enderecos.append(_endereco(i, parser)) 170 | return enderecos 171 | 172 | 173 | def _endereco(i: int, parser: configparser.ConfigParser) -> Endereco: 174 | section = f'Endereco{i + 1}' 175 | options = [ 176 | 'Tipo_Logradouro', 177 | 'Logradouro', 178 | 'Complemento', 179 | 'Bairro', 180 | 'Municipio', 181 | 'UF', 182 | 'CEP', 183 | 'IBGE_Municipio', 184 | 'IBGE_UF', 185 | ] 186 | kwargs = {} 187 | for option in options: 188 | if not parser.has_option(section, option): 189 | raise ACBrLibCEPErroResposta( 190 | 'Resposta mal formada; a resposta nao possui a ' 191 | f'informacao {option!r} na secao {section!r}' 192 | ) 193 | kwargs[option.lower()] = parser.get(section, option) 194 | return Endereco(**kwargs) 195 | -------------------------------------------------------------------------------- /acbrlib_python/cep/modelos.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # acbrlib_python/cep/modelos.py 4 | # 5 | # Copyright 2021 Base4 Sistemas 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | from dataclasses import dataclass 21 | 22 | 23 | @dataclass(frozen=True) 24 | class Endereco: 25 | tipo_logradouro: str 26 | logradouro: str 27 | complemento: str 28 | bairro: str 29 | municipio: str 30 | uf: str 31 | cep: str 32 | ibge_municipio: str 33 | ibge_uf: str 34 | -------------------------------------------------------------------------------- /acbrlib_python/constantes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # acbrlib_python/constantes.py 4 | # 5 | # Copyright 2021 Base4 Sistemas 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | from .cep.constantes import * # noqa: 21 | 22 | AUTO = 'auto' 23 | STANDARD_C = 'cdecl' 24 | WINDOWS_STDCALL = 'stdcall' 25 | 26 | CALLING_CONVENTIONS = ( 27 | (AUTO, 'Automático (pela extensão)'), 28 | (STANDARD_C, 'C Padrão (Cdecl)'), 29 | (WINDOWS_STDCALL, 'Windows Padrão (StdCall)'), 30 | ) 31 | 32 | 33 | CONVENCOES_CHAMADA = CALLING_CONVENTIONS 34 | 35 | BUFFER_LENGTH = 1024 36 | -------------------------------------------------------------------------------- /acbrlib_python/excecoes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # acbrlib_python/excecoes.py 4 | # 5 | # Copyright 2021 Base4 Sistemas 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | from unidecode import unidecode 21 | 22 | 23 | class ACBrLibException(Exception): 24 | def __init__(self, metodo=None, retorno=None, mensagem=None): 25 | if not mensagem: 26 | mensagem = f'Código de retorno inesperado: {retorno!r}' 27 | mensagem = f'{mensagem} (método {metodo!r} retornou {retorno!r})' 28 | self._metodo = metodo 29 | self._retorno = retorno 30 | super().__init__(unidecode(mensagem)) 31 | 32 | @property 33 | def metodo(self): 34 | return self._metodo 35 | 36 | @property 37 | def retorno(self): 38 | return self._retorno 39 | -------------------------------------------------------------------------------- /acbrlib_python/mixins.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # acbrlib_python/mixins.py 4 | # 5 | # Copyright 2021 Base4 Sistemas 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | from ctypes import byref 21 | from ctypes import c_int 22 | from ctypes import create_string_buffer 23 | 24 | from .constantes import BUFFER_LENGTH 25 | from .proto import ACBrLibMixin 26 | from .proto import read_string_buffer 27 | 28 | 29 | class ACBrLibCommonMixin(ACBrLibMixin): 30 | """ 31 | Fornece os "métodos da biblioteca" comuns a todos os sabores de 32 | bibliotecas ACBrLib: 33 | 34 | * ``XXX_Inicializar`` 35 | * ``XXX_Finalizar`` 36 | * ``XXX_UltimoRetorno`` 37 | * ``XXX_Nome`` 38 | * ``XXX_Versao`` 39 | 40 | Este mixin requer que a classe herde de 41 | :class:`~acbrlib_python.base.ACBrLibReferencia`. 42 | """ 43 | 44 | def inicializar(self, arq_config: str, chave_crypt: str) -> None: 45 | metodo = f'{self._prefixo}_Inicializar' 46 | retorno = self._invocar(metodo)(self._b(arq_config), self._b(chave_crypt)) 47 | if retorno == 0: 48 | return 49 | else: 50 | codigos_erro = { 51 | -1: 'Falha na inicialização da biblioteca', 52 | -5: 'Não foi possível localizar o arquivo INI informado', 53 | -6: 'Não foi possível encontrar o diretório do arquivo INI', 54 | } 55 | raise self._base_exception( 56 | metodo=metodo, 57 | retorno=retorno, 58 | mensagem=codigos_erro.get(retorno) 59 | ) 60 | 61 | def finalizar(self) -> None: 62 | metodo = f'{self._prefixo}_Finalizar' 63 | retorno = self._invocar(metodo)() 64 | if retorno == 0: 65 | return 66 | else: 67 | codigos_erro = { 68 | -2: 'Falha na finalização da biblioteca' 69 | } 70 | raise self._base_exception( 71 | metodo=metodo, 72 | retorno=retorno, 73 | mensagem=codigos_erro.get(retorno) 74 | ) 75 | 76 | def ultimo_retorno(self, buffer_len=BUFFER_LENGTH) -> str: 77 | metodo = f'{self._prefixo}_UltimoRetorno' 78 | resposta = create_string_buffer(buffer_len) 79 | tamanho = c_int(buffer_len) 80 | retorno = self._invocar(metodo)(resposta, byref(tamanho)) 81 | if retorno == 0: 82 | return self._s(resposta.value) 83 | else: 84 | codigos_erro = { 85 | -10: 'Falha na execução do método' 86 | } 87 | raise self._base_exception( 88 | metodo=metodo, 89 | retorno=retorno, 90 | mensagem=codigos_erro.get(retorno) 91 | ) 92 | 93 | def nome(self) -> str: 94 | metodo = f'{self._prefixo}_Nome' 95 | return read_string_buffer(self, metodo, buffer_len=1) 96 | 97 | def versao(self) -> str: 98 | metodo = f'{self._prefixo}_Versao' 99 | return read_string_buffer(self, metodo) 100 | 101 | 102 | class ACBrLibConfigMixin(ACBrLibMixin): 103 | """ 104 | Fornece os "métodos de configuração" comuns a todos os sabores de 105 | bibliotecas ACBrLib: 106 | 107 | * ``XXX_ConfigLer`` 108 | * ``XXX_ConfigGravar`` 109 | * ``XXX_ConfigLerValor`` 110 | * ``XXX_ConfigGravarValor`` 111 | * ``XXX_ConfigImportar`` 112 | * ``XXX_ConfigExportar`` 113 | 114 | Este mixin requer que a classe herde de 115 | :class:`~acbrlib_python.base.ACBrLibReferencia`. 116 | """ 117 | 118 | def config_ler(self, arq_config: str) -> None: 119 | metodo = f'{self._prefixo}_ConfigLer' 120 | retorno = self._invocar(metodo)(self._b(arq_config)) 121 | if retorno != 0: 122 | codigos_erro = { 123 | -5: 'Não foi possível localizar o arquivo INI informado', 124 | -6: 'Não foi possível encontrar o diretório do arquivo INI', 125 | -10: 'Houve uma falha na execução do método' 126 | } 127 | raise self._base_exception( 128 | metodo=metodo, 129 | retorno=retorno, 130 | mensagem=codigos_erro.get(retorno) 131 | ) 132 | 133 | def config_gravar(self, arq_config: str) -> None: 134 | metodo = f'{self._prefixo}_ConfigGravar' 135 | retorno = self._invocar(metodo)(self._b(arq_config)) 136 | if retorno != 0: 137 | codigos_erro = { 138 | -5: 'Não foi possível localizar o arquivo INI informado', 139 | -6: 'Não foi possível encontrar o diretório do arquivo INI', 140 | -10: 'Houve uma falha na execução do método' 141 | } 142 | raise self._base_exception( 143 | metodo=metodo, 144 | retorno=retorno, 145 | mensagem=codigos_erro.get(retorno) 146 | ) 147 | 148 | def config_ler_valor(self, sessao: str, chave: str) -> str: 149 | metodo = f'{self._prefixo}_ConfigLerValor' 150 | resposta = create_string_buffer(BUFFER_LENGTH) 151 | tamanho = c_int(BUFFER_LENGTH) 152 | retorno = self._invocar(metodo)( 153 | self._b(sessao), 154 | self._b(chave), 155 | resposta, 156 | byref(tamanho) 157 | ) 158 | if retorno == 0: 159 | return self._s(resposta.value) 160 | else: 161 | codigos_erro = { 162 | -1: 'A biblioteca não foi inicializada', 163 | -3: 'Erro ao ler a configuração informada', 164 | } 165 | raise self._base_exception( 166 | metodo=metodo, 167 | retorno=retorno, 168 | mensagem=codigos_erro.get(retorno) 169 | ) 170 | 171 | def config_gravar_valor(self, sessao: str, chave: str, valor: str) -> None: 172 | metodo = f'{self._prefixo}_ConfigGravarValor' 173 | retorno = self._invocar(metodo)(self._b(sessao), self._b(chave), self._b(valor)) 174 | if retorno != 0: 175 | codigos_erro = { 176 | -1: 'A biblioteca não foi inicializada', 177 | -3: 'Erro ao ler a configuração informada', 178 | } 179 | raise self._base_exception( 180 | metodo=metodo, 181 | retorno=retorno, 182 | mensagem=codigos_erro.get(retorno) 183 | ) 184 | 185 | def config_importar(self, arq_config: str) -> None: 186 | metodo = f'{self._prefixo}_ConfigImportar' 187 | retorno = self._invocar(metodo)(self._b(arq_config)) 188 | if retorno != 0: 189 | codigos_erro = { 190 | -5: 'Não foi possível localizar o arquivo INI informado', 191 | -6: 'Não foi possível encontrar o diretório do arquivo INI', 192 | -10: 'Houve uma falha na execução do método' 193 | } 194 | raise self._base_exception( 195 | metodo=metodo, 196 | retorno=retorno, 197 | mensagem=codigos_erro.get(retorno) 198 | ) 199 | 200 | def config_exportar(self) -> str: 201 | metodo = f'{self._prefixo}_ConfigExportar' 202 | resposta = create_string_buffer(BUFFER_LENGTH) 203 | tamanho = c_int(BUFFER_LENGTH) 204 | retorno = self._invocar(metodo)(resposta, byref(tamanho)) 205 | if retorno == 0: 206 | return self._s(resposta.value) 207 | else: 208 | codigos_erro = { 209 | -10: 'Houve uma falha na execução do método' 210 | } 211 | raise self._base_exception( 212 | metodo=metodo, 213 | retorno=retorno, 214 | mensagem=codigos_erro.get(retorno) 215 | ) 216 | -------------------------------------------------------------------------------- /acbrlib_python/proto.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # acbrlib_python/cep/proto.py 4 | # 5 | # Copyright 2021 Base4 Sistemas 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | import sys 21 | 22 | from ctypes import CDLL 23 | from ctypes import POINTER 24 | from ctypes import byref 25 | from ctypes import c_char_p 26 | from ctypes import c_int 27 | from ctypes import create_string_buffer 28 | 29 | from typing import Any 30 | from typing import List 31 | from typing import Mapping 32 | from typing import Optional 33 | from typing import Type 34 | from typing import Union 35 | 36 | from .constantes import AUTO 37 | from .constantes import BUFFER_LENGTH 38 | from .excecoes import ACBrLibException 39 | 40 | 41 | class Signature(object): 42 | """Descreve uma assinatura de função e o tipo de retorno.""" 43 | 44 | __slots__ = ('_argtypes', '_restype') 45 | 46 | def __init__( 47 | self, 48 | argtypes: List[Any], 49 | restype: Optional[Any] = None): 50 | self._argtypes = argtypes[:] 51 | self._restype = restype or c_int 52 | 53 | @property 54 | def argtypes(self): 55 | return self._argtypes[:] 56 | 57 | @property 58 | def restype(self): 59 | return self._restype 60 | 61 | 62 | class ReferenceLibrary(object): 63 | 64 | def __init__(self, library_path, calling_convention=AUTO, lazy_load=True): 65 | self._path = library_path 66 | self._calling_convention = calling_convention 67 | self._lazy_load = lazy_load 68 | self._ref = None if self._lazy_load else self._load_library() 69 | 70 | @property 71 | def ref(self): 72 | if self._ref is None: 73 | self._load_library() 74 | return self._ref 75 | 76 | def _load_library(self): 77 | self._ref = loader(self._path, self._calling_convention) 78 | 79 | 80 | class ACBrLibMixin: 81 | pass 82 | 83 | 84 | class ACBrLibReferencia(object): 85 | 86 | def __init__( 87 | self, 88 | prefixo: str, 89 | biblioteca: ReferenceLibrary, 90 | prototipos: Mapping[str, Signature], 91 | base_exception: Type[ACBrLibException], 92 | encoding: str = 'utf-8'): 93 | self._prefixo = prefixo 94 | self._biblioteca = biblioteca 95 | self._prototipos = prototipos 96 | self._base_exception = base_exception 97 | self._encoding = encoding 98 | 99 | def _invocar(self, metodo: str): 100 | if metodo not in self._prototipos: 101 | raise ValueError(f'Metodo/funcao desconhecido: {metodo}') 102 | proto = self._prototipos.get(metodo) 103 | fptr = getattr(self._biblioteca.ref, metodo) 104 | fptr.argtypes = proto.argtypes 105 | fptr.restype = proto.restype 106 | return fptr 107 | 108 | def _b(self, value: str) -> bytes: 109 | return value.encode(self._encoding) 110 | 111 | def _s(self, value: bytes) -> str: 112 | return value.decode(self._encoding) 113 | 114 | 115 | def read_string_buffer( 116 | impl: Union[ACBrLibReferencia, ACBrLibMixin], 117 | method_name: str, 118 | *args, 119 | **kwargs): 120 | """ 121 | Faz uma leitura de um buffer string considerando que o tamanho inicial do 122 | buffer possa ser insuficiente. Se for o caso, esta função invocará a 123 | leitura dos dados do último retorno, considerando o tamanho ideal do 124 | buffer que é indicado durante a primeira tentativa. 125 | 126 | O código usuário desta biblioteca não deveria ter que acessar diretamente 127 | esta função. 128 | 129 | :param impl: Instância da :class:`ACBrLibReferencia`. 130 | :param method_name: Nome do método a ser invocado. 131 | :param args: Os parâmetros posicionais a serem passados para o método. 132 | :param kwargs: Os parâmetros nomeados a serem passados parao método. 133 | Se houver um parâmetro chamado ``buffer_len`` (*int*), ele será 134 | utilizado como referência para o tamanho do buffer a ser lido. 135 | Se não for informado será usado o valor da constante 136 | :attr:`acbrlib_python.constantes.BUFFER_LENGTH`. 137 | 138 | :return: Retorna o buffer string já convertido para o encoding da 139 | implementação definido em :class:`ACBrLibReferencia`. 140 | """ 141 | buffer_len = kwargs.pop('buffer_len', BUFFER_LENGTH) 142 | str_buffer = create_string_buffer(buffer_len) 143 | int_size = c_int(buffer_len) 144 | mod_args = list(args) + [str_buffer, byref(int_size)] 145 | retval = getattr(impl, '_invocar')(method_name)(*mod_args, **kwargs) 146 | if retval == 0: 147 | if int_size.value > buffer_len: 148 | return impl.ultimo_retorno(buffer_len=int_size.value) 149 | else: 150 | return getattr(impl, '_s')(str_buffer.value) 151 | else: 152 | exc = getattr(impl, '_base_exception') 153 | raise exc(metodo=method_name, retorno=retval) 154 | 155 | 156 | def common_method_prototypes( 157 | prefix: str, 158 | excludes: Optional[List[str]] = None) -> Mapping[str, Signature]: 159 | """ 160 | Retorna o conjunto dos métodos que aparecem na documentação da 161 | ACBrLib como "Métodos da Biblioteca". O código usuário desta 162 | biblioteca não deveria ter que acessar diretamente esta função. 163 | 164 | :param prefix: Prefixo de três letras que identifica a biblioteca, 165 | como "CEP" ou "NFE" por exemplo. 166 | 167 | :param excludes: Nomes de métodos que devem ser removidos do 168 | resultado. Deve ser uma lista especificando o nome completo, 169 | incluindo o prefixo, por exemplo "CEP_UltimoRetorno" para 170 | que esse protótipo de função seja excluído do resultado. 171 | 172 | :return: Um dicionário contendo os nomes de função e suas 173 | assinaturas de parâmetros (protótipos de funções). 174 | """ 175 | prototypes = { 176 | f'{prefix}_Inicializar': Signature([c_char_p, c_char_p]), # eArqConfig, eChaveCrypt 177 | f'{prefix}_Finalizar': Signature([]), 178 | f'{prefix}_UltimoRetorno': Signature([c_char_p, POINTER(c_int)]), # sMensagem, esTamanho 179 | f'{prefix}_Nome': Signature([c_char_p, POINTER(c_int)]), # sNome, esTamanho 180 | f'{prefix}_Versao': Signature([c_char_p, POINTER(c_int)]), # sVersao, esTamanho 181 | } 182 | if excludes: 183 | for name in excludes: 184 | prototypes.pop(name) 185 | return prototypes 186 | 187 | 188 | def config_method_prototypes( 189 | prefix: str, 190 | excludes: Optional[List[str]] = None) -> Mapping[str, Signature]: 191 | """ 192 | Retorna o conjunto dos métodos que aparecem na documentação da 193 | ACBrLib como "Métodos da Configuração". O código usuário desta 194 | biblioteca não deveria ter que acessar diretamente esta função. 195 | 196 | :param prefix: Prefixo de três letras que identifica a biblioteca, 197 | como "CEP" ou "NFE" por exemplo. 198 | 199 | :param excludes: Nomes de métodos que devem ser removidos do 200 | resultado. Deve ser uma lista especificando o nome completo, 201 | incluindo o prefixo, por exemplo "DIS_ConfigImportar" para 202 | que esse protótipo de função seja excluído do resultado. 203 | 204 | :return: Um dicionário contendo os nomes de função e suas 205 | assinaturas de parâmetros (protótipos de funções). 206 | """ 207 | prototypes = { 208 | f'{prefix}_ConfigLer': Signature([c_char_p]), # eArqConfig 209 | f'{prefix}_ConfigGravar': Signature([c_char_p]), # eArqConfig 210 | f'{prefix}_ConfigLerValor': Signature([ 211 | c_char_p, # eSessao 212 | c_char_p, # eChave 213 | c_char_p, # sValor 214 | POINTER(c_int) # esTamanho 215 | ]), 216 | f'{prefix}_ConfigGravarValor': Signature([c_char_p, c_char_p, c_char_p]), # eSessao, eChave, sValor 217 | f'{prefix}_ConfigImportar': Signature([c_char_p]), # eArqConfig 218 | f'{prefix}_ConfigExportar': Signature([c_char_p, POINTER(c_int)]), # sMensagem, esTamanho 219 | } 220 | if excludes: 221 | for name in excludes: 222 | prototypes.pop(name) 223 | return prototypes 224 | 225 | 226 | def loader(path: str, calling_convention: str) -> CDLL: 227 | """ 228 | Carrega uma biblioteca (DLL/shared object) no caminho indicado. 229 | 230 | :param path: Caminho completo para biblioteca. 231 | 232 | :param calling_convention: Convenção de chamada. 233 | Veja as constantes definidas em 234 | :attr:`acbrlib_python.constantes.CONVENCOES_CHAMADA`. 235 | 236 | :return: Uma referência para ``ctypes.CDLL`` carregada, 237 | conforme a convenção de chamada (*CDECL* ou *StdCall*). 238 | 239 | """ 240 | name = f'_loader_{calling_convention}' 241 | loader_func = getattr(sys.modules[__name__], name, None) 242 | if not loader_func: 243 | raise ValueError( 244 | f'Unexpected calling convention; got {calling_convention!r}' 245 | ) 246 | return loader_func(path, calling_convention) 247 | 248 | 249 | def _loader_auto(path: str, calling_convention: str) -> CDLL: 250 | if path.endswith(('.DLL', '.dll')): 251 | return _loader_stdcall(path, calling_convention) 252 | else: 253 | return _loader_cdecl(path, calling_convention) 254 | 255 | 256 | def _loader_stdcall(path: str, calling_convention: str) -> CDLL: 257 | from ctypes import WinDLL 258 | return WinDLL(path) 259 | 260 | 261 | def _loader_cdecl(path: str, calling_convention: str) -> CDLL: 262 | return CDLL(path) 263 | -------------------------------------------------------------------------------- /exemplos/cep.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # exemplos/cep.py 4 | # 5 | # Copyright 2021 Base4 Sistemas 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | """ 21 | Invoque este exemplo, modificando os valores padrão das variáveis 22 | diretamente neste código ou modificando os valores das variáveis de 23 | ambiente na chamada, por exemplo: 24 | 25 | $ ACBRLIB_INI_PATH=/home/user/ACBR.INI python exemplos/cep.py 26 | 27 | Não considere este exemplo completo ou definitivo. Dê uma boa olhada 28 | antes de executá-lo. Sempre tomamos o cuidado de 29 | não fazer nada que possa causar problemas. 30 | """ 31 | 32 | import os 33 | 34 | from acbrlib_python import ACBrLibCEP 35 | 36 | 37 | ACBRLIB_PATH = os.getenv('ACBRLIB_PATH', '/usr/lib/libacbrcep64.so') 38 | ACBRLIB_INI_PATH = os.getenv('ACBRLIB_INI_PATH', '') 39 | ACBRLIB_CHAVE_CRYPT = os.getenv('ACBRLIB_CHAVE_CRYPT', '') 40 | 41 | 42 | def iniciacao_simples(): 43 | cep = ACBrLibCEP.usar(ACBRLIB_PATH) 44 | cep.inicializar(ACBRLIB_INI_PATH, ACBRLIB_CHAVE_CRYPT) 45 | print(f'CEP_Nome: {cep.nome()}') 46 | print(f'CEP_Versao: {cep.versao()}') 47 | print(f'CEP_UltimoRetorno: {cep.ultimo_retorno()}') 48 | cep.finalizar() 49 | 50 | 51 | def iniciacao_simples_e_segura(): 52 | # o context manager garante a finalização 53 | with ACBrLibCEP.usando( 54 | ACBRLIB_PATH, 55 | arq_config=ACBRLIB_INI_PATH, 56 | chave_crypt=ACBRLIB_CHAVE_CRYPT) as cep: 57 | print(f'{cep.nome()}, versao {cep.versao()}') 58 | print(f'Último retorno: {cep.ultimo_retorno()}') 59 | 60 | 61 | def consultar_por_cep(): 62 | with ACBrLibCEP.usando( 63 | ACBRLIB_PATH, 64 | arq_config=ACBRLIB_INI_PATH, 65 | chave_crypt=ACBRLIB_CHAVE_CRYPT) as cep: 66 | enderecos = cep.buscar_por_cep('18270170') 67 | for endereco in enderecos: 68 | print(endereco) 69 | 70 | 71 | def consultar_por_logradouro(): 72 | with ACBrLibCEP.usando( 73 | ACBRLIB_PATH, 74 | arq_config=ACBRLIB_INI_PATH, 75 | chave_crypt=ACBRLIB_CHAVE_CRYPT) as cep: 76 | enderecos = cep.buscar_por_logradouro( 77 | logradouro='Rua Brasil', 78 | municipio='Catanduva', 79 | uf='SP' 80 | ) 81 | for ender in enderecos: 82 | print(ender) 83 | 84 | 85 | def manipulacao_de_configuracao(): 86 | with ACBrLibCEP.usando(ACBRLIB_PATH) as cep: 87 | cep.config_ler(ACBRLIB_INI_PATH) 88 | # valor = cep.config_ler_valor('CEP', 'WebService') 89 | # print(f'CEP/WebService: {valor!r}') 90 | valor = cep.config_ler_valor('Principal', 'LogPath') 91 | print(f'Principal/LogPath: {valor!r}') 92 | 93 | 94 | if __name__ == '__main__': 95 | # iniciacao_simples() 96 | # iniciacao_simples_e_segura() 97 | # consultar_por_cep() 98 | consultar_por_logradouro() 99 | # manipulacao_de_configuracao() 100 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "appnope" 3 | version = "0.1.2" 4 | description = "Disable App Nap on macOS >= 10.9" 5 | category = "dev" 6 | optional = false 7 | python-versions = "*" 8 | 9 | [[package]] 10 | name = "atomicwrites" 11 | version = "1.4.0" 12 | description = "Atomic file writes." 13 | category = "dev" 14 | optional = false 15 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 16 | 17 | [[package]] 18 | name = "attrs" 19 | version = "21.2.0" 20 | description = "Classes Without Boilerplate" 21 | category = "dev" 22 | optional = false 23 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 24 | 25 | [package.extras] 26 | dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] 27 | docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] 28 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] 29 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] 30 | 31 | [[package]] 32 | name = "backcall" 33 | version = "0.2.0" 34 | description = "Specifications for callback functions passed in to an API" 35 | category = "dev" 36 | optional = false 37 | python-versions = "*" 38 | 39 | [[package]] 40 | name = "colorama" 41 | version = "0.4.4" 42 | description = "Cross-platform colored terminal text." 43 | category = "dev" 44 | optional = false 45 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 46 | 47 | [[package]] 48 | name = "decorator" 49 | version = "5.1.0" 50 | description = "Decorators for Humans" 51 | category = "dev" 52 | optional = false 53 | python-versions = ">=3.5" 54 | 55 | [[package]] 56 | name = "iniconfig" 57 | version = "1.1.1" 58 | description = "iniconfig: brain-dead simple config-ini parsing" 59 | category = "dev" 60 | optional = false 61 | python-versions = "*" 62 | 63 | [[package]] 64 | name = "ipython" 65 | version = "7.28.0" 66 | description = "IPython: Productive Interactive Computing" 67 | category = "dev" 68 | optional = false 69 | python-versions = ">=3.7" 70 | 71 | [package.dependencies] 72 | appnope = {version = "*", markers = "sys_platform == \"darwin\""} 73 | backcall = "*" 74 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 75 | decorator = "*" 76 | jedi = ">=0.16" 77 | matplotlib-inline = "*" 78 | pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} 79 | pickleshare = "*" 80 | prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" 81 | pygments = "*" 82 | traitlets = ">=4.2" 83 | 84 | [package.extras] 85 | all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"] 86 | doc = ["Sphinx (>=1.3)"] 87 | kernel = ["ipykernel"] 88 | nbconvert = ["nbconvert"] 89 | nbformat = ["nbformat"] 90 | notebook = ["notebook", "ipywidgets"] 91 | parallel = ["ipyparallel"] 92 | qtconsole = ["qtconsole"] 93 | test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.17)"] 94 | 95 | [[package]] 96 | name = "jedi" 97 | version = "0.18.0" 98 | description = "An autocompletion tool for Python that can be used for text editors." 99 | category = "dev" 100 | optional = false 101 | python-versions = ">=3.6" 102 | 103 | [package.dependencies] 104 | parso = ">=0.8.0,<0.9.0" 105 | 106 | [package.extras] 107 | qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] 108 | testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<6.0.0)"] 109 | 110 | [[package]] 111 | name = "matplotlib-inline" 112 | version = "0.1.3" 113 | description = "Inline Matplotlib backend for Jupyter" 114 | category = "dev" 115 | optional = false 116 | python-versions = ">=3.5" 117 | 118 | [package.dependencies] 119 | traitlets = "*" 120 | 121 | [[package]] 122 | name = "packaging" 123 | version = "21.0" 124 | description = "Core utilities for Python packages" 125 | category = "dev" 126 | optional = false 127 | python-versions = ">=3.6" 128 | 129 | [package.dependencies] 130 | pyparsing = ">=2.0.2" 131 | 132 | [[package]] 133 | name = "parso" 134 | version = "0.8.2" 135 | description = "A Python Parser" 136 | category = "dev" 137 | optional = false 138 | python-versions = ">=3.6" 139 | 140 | [package.extras] 141 | qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] 142 | testing = ["docopt", "pytest (<6.0.0)"] 143 | 144 | [[package]] 145 | name = "pexpect" 146 | version = "4.8.0" 147 | description = "Pexpect allows easy control of interactive console applications." 148 | category = "dev" 149 | optional = false 150 | python-versions = "*" 151 | 152 | [package.dependencies] 153 | ptyprocess = ">=0.5" 154 | 155 | [[package]] 156 | name = "pickleshare" 157 | version = "0.7.5" 158 | description = "Tiny 'shelve'-like database with concurrency support" 159 | category = "dev" 160 | optional = false 161 | python-versions = "*" 162 | 163 | [[package]] 164 | name = "pluggy" 165 | version = "1.0.0" 166 | description = "plugin and hook calling mechanisms for python" 167 | category = "dev" 168 | optional = false 169 | python-versions = ">=3.6" 170 | 171 | [package.extras] 172 | dev = ["pre-commit", "tox"] 173 | testing = ["pytest", "pytest-benchmark"] 174 | 175 | [[package]] 176 | name = "prompt-toolkit" 177 | version = "3.0.20" 178 | description = "Library for building powerful interactive command lines in Python" 179 | category = "dev" 180 | optional = false 181 | python-versions = ">=3.6.2" 182 | 183 | [package.dependencies] 184 | wcwidth = "*" 185 | 186 | [[package]] 187 | name = "ptyprocess" 188 | version = "0.7.0" 189 | description = "Run a subprocess in a pseudo terminal" 190 | category = "dev" 191 | optional = false 192 | python-versions = "*" 193 | 194 | [[package]] 195 | name = "py" 196 | version = "1.10.0" 197 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 198 | category = "dev" 199 | optional = false 200 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 201 | 202 | [[package]] 203 | name = "pygments" 204 | version = "2.10.0" 205 | description = "Pygments is a syntax highlighting package written in Python." 206 | category = "dev" 207 | optional = false 208 | python-versions = ">=3.5" 209 | 210 | [[package]] 211 | name = "pyparsing" 212 | version = "2.4.7" 213 | description = "Python parsing module" 214 | category = "dev" 215 | optional = false 216 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 217 | 218 | [[package]] 219 | name = "pytest" 220 | version = "6.2.5" 221 | description = "pytest: simple powerful testing with Python" 222 | category = "dev" 223 | optional = false 224 | python-versions = ">=3.6" 225 | 226 | [package.dependencies] 227 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} 228 | attrs = ">=19.2.0" 229 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 230 | iniconfig = "*" 231 | packaging = "*" 232 | pluggy = ">=0.12,<2.0" 233 | py = ">=1.8.2" 234 | toml = "*" 235 | 236 | [package.extras] 237 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] 238 | 239 | [[package]] 240 | name = "toml" 241 | version = "0.10.2" 242 | description = "Python Library for Tom's Obvious, Minimal Language" 243 | category = "dev" 244 | optional = false 245 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 246 | 247 | [[package]] 248 | name = "traitlets" 249 | version = "5.1.0" 250 | description = "Traitlets Python configuration system" 251 | category = "dev" 252 | optional = false 253 | python-versions = ">=3.7" 254 | 255 | [package.extras] 256 | test = ["pytest"] 257 | 258 | [[package]] 259 | name = "unidecode" 260 | version = "1.3.2" 261 | description = "ASCII transliterations of Unicode text" 262 | category = "main" 263 | optional = false 264 | python-versions = ">=3.5" 265 | 266 | [[package]] 267 | name = "wcwidth" 268 | version = "0.2.5" 269 | description = "Measures the displayed width of unicode strings in a terminal" 270 | category = "dev" 271 | optional = false 272 | python-versions = "*" 273 | 274 | [metadata] 275 | lock-version = "1.1" 276 | python-versions = "^3.9" 277 | content-hash = "047b6d30629dc03ed9c9656e19ce04dad9bcbd619f04f161e7a40a93f6e12cb6" 278 | 279 | [metadata.files] 280 | appnope = [ 281 | {file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"}, 282 | {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"}, 283 | ] 284 | atomicwrites = [ 285 | {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, 286 | {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, 287 | ] 288 | attrs = [ 289 | {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, 290 | {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, 291 | ] 292 | backcall = [ 293 | {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, 294 | {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, 295 | ] 296 | colorama = [ 297 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, 298 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, 299 | ] 300 | decorator = [ 301 | {file = "decorator-5.1.0-py3-none-any.whl", hash = "sha256:7b12e7c3c6ab203a29e157335e9122cb03de9ab7264b137594103fd4a683b374"}, 302 | {file = "decorator-5.1.0.tar.gz", hash = "sha256:e59913af105b9860aa2c8d3272d9de5a56a4e608db9a2f167a8480b323d529a7"}, 303 | ] 304 | iniconfig = [ 305 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, 306 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, 307 | ] 308 | ipython = [ 309 | {file = "ipython-7.28.0-py3-none-any.whl", hash = "sha256:f16148f9163e1e526f1008d7c8d966d9c15600ca20d1a754287cf96d00ba6f1d"}, 310 | {file = "ipython-7.28.0.tar.gz", hash = "sha256:2097be5c814d1b974aea57673176a924c4c8c9583890e7a5f082f547b9975b11"}, 311 | ] 312 | jedi = [ 313 | {file = "jedi-0.18.0-py2.py3-none-any.whl", hash = "sha256:18456d83f65f400ab0c2d3319e48520420ef43b23a086fdc05dff34132f0fb93"}, 314 | {file = "jedi-0.18.0.tar.gz", hash = "sha256:92550a404bad8afed881a137ec9a461fed49eca661414be45059329614ed0707"}, 315 | ] 316 | matplotlib-inline = [ 317 | {file = "matplotlib-inline-0.1.3.tar.gz", hash = "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee"}, 318 | {file = "matplotlib_inline-0.1.3-py3-none-any.whl", hash = "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c"}, 319 | ] 320 | packaging = [ 321 | {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"}, 322 | {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"}, 323 | ] 324 | parso = [ 325 | {file = "parso-0.8.2-py2.py3-none-any.whl", hash = "sha256:a8c4922db71e4fdb90e0d0bc6e50f9b273d3397925e5e60a717e719201778d22"}, 326 | {file = "parso-0.8.2.tar.gz", hash = "sha256:12b83492c6239ce32ff5eed6d3639d6a536170723c6f3f1506869f1ace413398"}, 327 | ] 328 | pexpect = [ 329 | {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, 330 | {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, 331 | ] 332 | pickleshare = [ 333 | {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, 334 | {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, 335 | ] 336 | pluggy = [ 337 | {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, 338 | {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, 339 | ] 340 | prompt-toolkit = [ 341 | {file = "prompt_toolkit-3.0.20-py3-none-any.whl", hash = "sha256:6076e46efae19b1e0ca1ec003ed37a933dc94b4d20f486235d436e64771dcd5c"}, 342 | {file = "prompt_toolkit-3.0.20.tar.gz", hash = "sha256:eb71d5a6b72ce6db177af4a7d4d7085b99756bf656d98ffcc4fecd36850eea6c"}, 343 | ] 344 | ptyprocess = [ 345 | {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, 346 | {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, 347 | ] 348 | py = [ 349 | {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, 350 | {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, 351 | ] 352 | pygments = [ 353 | {file = "Pygments-2.10.0-py3-none-any.whl", hash = "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380"}, 354 | {file = "Pygments-2.10.0.tar.gz", hash = "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"}, 355 | ] 356 | pyparsing = [ 357 | {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, 358 | {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, 359 | ] 360 | pytest = [ 361 | {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, 362 | {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, 363 | ] 364 | toml = [ 365 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 366 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 367 | ] 368 | traitlets = [ 369 | {file = "traitlets-5.1.0-py3-none-any.whl", hash = "sha256:03f172516916220b58c9f19d7f854734136dd9528103d04e9bf139a92c9f54c4"}, 370 | {file = "traitlets-5.1.0.tar.gz", hash = "sha256:bd382d7ea181fbbcce157c133db9a829ce06edffe097bcf3ab945b435452b46d"}, 371 | ] 372 | unidecode = [ 373 | {file = "Unidecode-1.3.2-py3-none-any.whl", hash = "sha256:215fe33c9d1c889fa823ccb66df91b02524eb8cc8c9c80f9c5b8129754d27829"}, 374 | {file = "Unidecode-1.3.2.tar.gz", hash = "sha256:669898c1528912bcf07f9819dc60df18d057f7528271e31f8ec28cc88ef27504"}, 375 | ] 376 | wcwidth = [ 377 | {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, 378 | {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, 379 | ] 380 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "acbrlib-python" 3 | version = "0.1.0" 4 | description = "Camada de abstração para acesso à ACBrLib em Python" 5 | license = "Apache-2.0" 6 | repository = "https://github.com/base4sistemas/acbrlib-python" 7 | authors = ["Daniel Gonçalves "] 8 | readme = "README.rst" 9 | classifiers = [ 10 | "Development Status :: 1 - Planning", 11 | "Environment :: Other Environment", 12 | "Intended Audience :: Developers", 13 | "License :: OSI Approved :: Apache Software License", 14 | "Natural Language :: Portuguese (Brazilian)", 15 | "Operating System :: OS Independent", 16 | "Programming Language :: Python", 17 | "Programming Language :: Python :: 3", 18 | "Topic :: Office/Business", 19 | "Topic :: Office/Business :: Financial", 20 | "Topic :: Office/Business :: Financial :: Point-Of-Sale", 21 | "Topic :: Printing", 22 | "Topic :: Software Development", 23 | "Topic :: Software Development :: Libraries", 24 | ] 25 | 26 | [tool.poetry.dependencies] 27 | python = "^3.9" 28 | Unidecode = "^1.3.2" 29 | 30 | [tool.poetry.dev-dependencies] 31 | pytest = "*" 32 | ipython = "^7.28.0" 33 | 34 | [tool.poetry.urls] 35 | "Bug Tracker" = "https://github.com/base4sistemas/acbrlib-python/issues" 36 | 37 | [build-system] 38 | requires = ["poetry-core>=1.0.0"] 39 | build-backend = "poetry.core.masonry.api" 40 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # tests/__init__.py 4 | # 5 | # Copyright 2021 Base4 Sistemas 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | -------------------------------------------------------------------------------- /tests/cep/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # tests/cep/__init__.py 4 | # 5 | # Copyright 2021 Base4 Sistemas 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # -------------------------------------------------------------------------------- /tests/cep/test_resposta.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # tests/cep/test_resposta.py 4 | # 5 | # Copyright 2021 Base4 Sistemas 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | import pytest 21 | 22 | from acbrlib_python.cep.impl import processar_resposta 23 | from acbrlib_python.cep.excecoes import ACBrLibCEPErroResposta 24 | 25 | 26 | def test_resposta_sem_resultados(): 27 | """Uma resposta sem resultados deve resultar uma lista vazia.""" 28 | conteudo = [ 29 | '[CEP]', 30 | 'Quantidade=0', 31 | ] 32 | enderecos = processar_resposta('\n'.join(conteudo)) 33 | assert len(enderecos) == 0 34 | 35 | 36 | def test_resposta_com_um_resultado(): 37 | """Resposta normal, contendo um resultado.""" 38 | conteudo = [ 39 | '[Endereco1]', 40 | 'Bairro = Centro', 41 | 'CEP = 18270-170', 42 | 'Complemento =', 43 | 'IBGE_Municipio = 3554003', 44 | 'IBGE_UF = 35', 45 | 'Logradouro = Rua Coronel Aureliano de Camargo', 46 | 'Municipio = Tatuí', 47 | 'Tipo_Logradouro =', 48 | 'UF = SP', 49 | '', 50 | '[CEP]', 51 | 'Quantidade = 1', 52 | ] 53 | enderecos = processar_resposta('\n'.join(conteudo)) 54 | assert len(enderecos) == 1 55 | 56 | e = enderecos[0] 57 | assert e.tipo_logradouro == '' 58 | assert e.logradouro == 'Rua Coronel Aureliano de Camargo' 59 | assert e.complemento == '' 60 | assert e.bairro == 'Centro' 61 | assert e.municipio == 'Tatuí' 62 | assert e.uf == 'SP' 63 | assert e.cep == '18270-170' 64 | assert e.ibge_municipio == '3554003' 65 | assert e.ibge_uf == '35' 66 | 67 | 68 | def test_resposta_mal_formada_sem_quantidade(): 69 | """ 70 | Testa uma resposta que não possui a seção que indica a quantidade de 71 | endereços encontrados. 72 | """ 73 | conteudo = [ 74 | '[Endereco1]', 75 | 'Tipo_Logradouro = Rua', 76 | ] 77 | with pytest.raises(ACBrLibCEPErroResposta): 78 | processar_resposta('\n'.join(conteudo)) 79 | 80 | 81 | def test_resposta_mal_formada_sem_enderecos(): 82 | """ 83 | Testa uma resposta que indica a existência de 1 endereço, mas que não 84 | possui nenhuma seção ``EnderecoN``. 85 | """ 86 | conteudo = [ 87 | '[CEP]', 88 | 'Quantidade=1', 89 | ] 90 | with pytest.raises(ACBrLibCEPErroResposta): 91 | processar_resposta('\n'.join(conteudo)) 92 | -------------------------------------------------------------------------------- /tests/test_acbrlib_python.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # tests/test_acbrlib_python.py 4 | # 5 | # Copyright 2021 Base4 Sistemas 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | from acbrlib_python import __version__ 21 | 22 | 23 | def test_version(): 24 | assert __version__ == '0.1.0' 25 | -------------------------------------------------------------------------------- /tests/test_proto.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # tests/test_proto.py 4 | # 5 | # Copyright 2021 Base4 Sistemas 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | from acbrlib_python import proto 21 | from acbrlib_python.constantes import AUTO 22 | from acbrlib_python.proto import ReferenceLibrary 23 | from acbrlib_python.proto import Signature 24 | from acbrlib_python.proto import common_method_prototypes 25 | from acbrlib_python.proto import config_method_prototypes 26 | 27 | 28 | class _FakeCDLL: 29 | 30 | def __init__(self, path, calling_convention): 31 | self._path = path 32 | self._calling_convention = calling_convention 33 | 34 | 35 | def test_referencelibrary_class_auto_cdecl(monkeypatch): 36 | def mockreturn(path, calling_convention): 37 | return _FakeCDLL(path, calling_convention) 38 | monkeypatch.setattr(proto, 'loader', mockreturn) 39 | lib = ReferenceLibrary('/var/lib.so') 40 | assert isinstance(lib.ref, _FakeCDLL) 41 | assert lib._path == '/var/lib.so' 42 | assert lib._calling_convention == AUTO 43 | assert lib._lazy_load is True 44 | 45 | 46 | def test_signature_class(): 47 | original_args = ['a', 'b', 'c'] 48 | s = Signature(original_args, restype='t') 49 | assert 'a' in s.argtypes 50 | assert 'b' in s.argtypes 51 | assert 'c' in s.argtypes 52 | assert 't' == s.restype 53 | 54 | # a lista em argtypes deve ser uma cópia da lista de 55 | # argumentos informada e, portanto, imutável externamente 56 | original_args.append('x') 57 | assert 'x' not in s.argtypes 58 | 59 | ext_args = s.argtypes 60 | ext_args.append('x') 61 | assert 'x' not in s.argtypes 62 | 63 | 64 | def test_common_method_prototypes_all(): 65 | res = common_method_prototypes('CEP') 66 | assert 'CEP_Inicializar' in res 67 | assert 'CEP_Finalizar' in res 68 | assert 'CEP_UltimoRetorno' in res 69 | assert 'CEP_Nome' in res 70 | assert 'CEP_Versao' in res 71 | 72 | 73 | def test_common_method_prototypes_excluded(): 74 | res = common_method_prototypes('CEP', excludes=['CEP_Finalizar']) 75 | assert 'CEP_Finalizar' not in res 76 | 77 | 78 | def test_config_method_prototypes_all(): 79 | res = config_method_prototypes('NFE') 80 | assert 'NFE_ConfigLer' in res 81 | assert 'NFE_ConfigGravar' in res 82 | assert 'NFE_ConfigLerValor' in res 83 | assert 'NFE_ConfigGravarValor' in res 84 | assert 'NFE_ConfigImportar' in res 85 | assert 'NFE_ConfigExportar' in res 86 | 87 | 88 | def test_config_method_prototypes_excluded(): 89 | res = config_method_prototypes( 90 | 'DIS', 91 | excludes=['DIS_ConfigImportar', 'DIS_ConfigExportar'] 92 | ) 93 | assert 'DIS_ConfigImportar' not in res 94 | assert 'DIS_ConfigExportar' not in res 95 | --------------------------------------------------------------------------------