diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 000000000..fa1f9289b --- /dev/null +++ b/.coveragerc @@ -0,0 +1,30 @@ +# .coveragerc to control coverage.py +[run] +branch = True +source = easyocr +omit = + +[paths] +source = + */site-packages/ + +[report] +# Require 100% coverage for tests to succeed +fail_under = 100 + +# Regexes for lines to exclude from consideration +exclude_lines = + # Have to re-enable the standard pragma + pragma: no cover + + # Don't complain about missing debug-only code: + def __repr__ + if self\.debug + + # Don't complain if tests don't hit defensive assertion code: + raise AssertionError + raise NotImplementedError + + # Don't complain if non-runnable code isn't run: + if 0: + if __name__ == .__main__.: diff --git a/.gitignore b/.gitignore index 683938b5b..06881ed17 100644 --- a/.gitignore +++ b/.gitignore @@ -86,7 +86,7 @@ celerybeat-schedule .venv venv/ ENV/ - +.easyocr_venv # Spyder project settings .spyderproject .spyproject @@ -107,3 +107,11 @@ ENV/ .vscode/ .vs/ .idea/ + +# Personnal files +easy_ocr_dev.ipynb +easy_ocr_dev.py +notes_persos.txt + +# virtual environment: +.easyocr_venv diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..e767119ed --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,56 @@ +exclude: '^docs/conf.py' + +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: trailing-whitespace + - id: check-added-large-files + - id: check-ast + - id: check-json + - id: check-merge-conflict + - id: check-xml + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + - id: requirements-txt-fixer + - id: mixed-line-ending + args: ['--fix=auto'] + exclude: tests + +- repo: https://github.com/myint/autoflake + rev: v1.4 + hooks: + - id: autoflake + args: [ + --in-place, + --remove-all-unused-imports, + --remove-unused-variables, + ] + exclude: tests + +- repo: https://github.com/pycqa/isort + rev: 5.10.1 + hooks: + - id: isort + exclude: tests + +- repo: https://github.com/psf/black + rev: 22.6.0 + hooks: + - id: black + language_version: python3 + exclude: tests + +# - repo: https://github.com/PyCQA/flake8 +# rev: 4.0.1 +# hooks: +# - id: flake8 +# additional_dependencies: [wemake-python-styleguide] # Comment to disable wemake +# exclude: tests + +- repo: https://github.com/pre-commit/mirrors-mypy + rev: v0.971 + hooks: + - id: mypy + exclude: tests diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 000000000..21b08145f --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,23 @@ +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/conf.py + +# Build documentation with MkDocs +#mkdocs: +# configuration: mkdocs.yml + +# Optionally build your docs in additional formats such as PDF +formats: + - pdf + +python: + version: 3.8 + install: + - requirements: docs/requirements.txt + - {path: ., method: pip} diff --git a/MANIFEST.in b/MANIFEST.in index 6d6f4de9b..145e71016 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ -include LICENSE.txt +include LICENSE include README.md include easyocr/model/* diff --git a/easyocr/__init__.py b/easyocr/__init__.py index 56260c993..928d799fe 100644 --- a/easyocr/__init__.py +++ b/easyocr/__init__.py @@ -1,3 +1,16 @@ -from .easyocr import Reader +import sys + +if sys.version_info[:2] >= (3, 8): + from importlib.metadata import PackageNotFoundError, version +else: + from importlib_metadata import PackageNotFoundError, version -__version__ = '1.6.2' +dist_name = "easyocr" +try: + __version__ = version(dist_name) +except PackageNotFoundError: + __version__ = "unknown" +finally: + del version, PackageNotFoundError + +from .easyocr import Reader diff --git a/easyocr/scripts/compile_dbnet_dcn.py b/easyocr/scripts/compile_dbnet_dcn.py index f916016e4..23f6875ec 100644 --- a/easyocr/scripts/compile_dbnet_dcn.py +++ b/easyocr/scripts/compile_dbnet_dcn.py @@ -1,76 +1,115 @@ -''' +""" Created by Jaided AI Released Date: 19/08/2022 Description: A wrapper for DCN operator for DBNet. This script is called inside the setup.py of EasyOCR. It can also be called as a standalone script to compile the operator manually. -''' -import os +""" import glob -from datetime import datetime +import os import subprocess +import sys +from datetime import datetime -def print_error(errors, log_path): + +def print_error(errors, log_path): if not isinstance(errors, list): errors = [errors] - errors = [error if isinstance(error, bytes) else error.encode('utf-8') for error in errors] + errors = [ + error if isinstance(error, bytes) else error.encode("utf-8") for error in errors + ] url = "https://github.com/JaidedAI/EasyOCR/tree/master/easyocr/DBNet" print("Failed to compile dcn operator for DBNet.") with open(log_path, "wb") as fid: - fid.write((datetime.now().strftime("%H:%M:%S - %d %b %Y") + "\n").encode('utf-8')) - fid.write("Failed to compile dcn operator for DBNet with the following error.\n".encode('utf-8')) - fid.write(("#"*42 + '\n').encode('utf-8')) + fid.write( + (datetime.now().strftime("%H:%M:%S - %d %b %Y") + "\n").encode("utf-8") + ) + fid.write( + "Failed to compile dcn operator for DBNet with the following error.\n".encode( + "utf-8" + ) + ) + fid.write(("#" * 42 + "\n").encode("utf-8")) [fid.write(error) for error in errors] print("Error message can be found in {}.".format(os.path.abspath(log_path))) - print("#"*42) + print("#" * 42) print("EasyOCR can still be used with CRAFT text detector (default).") - print("To use DBNet text detector, please check {} for troubleshoot and compile dcn operator manually.".format(url)) + print( + "To use DBNet text detector, please check {} for troubleshoot and compile dcn operator manually.".format( + url + ) + ) + def print_success(text, log_path): with open(log_path, "wb") as fid: - fid.write((datetime.now().strftime("%H:%M:%S - %d %b %Y") + "\n").encode('utf-8')) - fid.write((text + "\n").encode('utf-8')) + fid.write( + (datetime.now().strftime("%H:%M:%S - %d %b %Y") + "\n").encode("utf-8") + ) + fid.write((text + "\n").encode("utf-8")) print(text) + def validate_compilation(parent_dir, log_path, cpu_or_cuda): - dcn_dir = os.path.join(parent_dir, 'DBNet', 'assets', 'ops', 'dcn') - #Just to be safe, check explicitly. - if cpu_or_cuda == 'cpu': - conv_cpu_exist = glob.glob(os.path.join(dcn_dir, 'deform_conv_cpu.*.so')) - pool_cpu_exist = glob.glob(os.path.join(dcn_dir, 'deform_pool_cpu.*.so')) - success_message = "DCN CPU operator is compiled successfully at {}.".format(os.path.abspath(os.path.join(parent_dir,'DBNet'))) - print_success(success_message, log_path) - return conv_cpu_exist and pool_cpu_exist - elif cpu_or_cuda == 'cuda': - conv_cuda_exist = glob.glob(os.path.join(dcn_dir, 'deform_conv_cuda.*.so')) - pool_cuda_exist = glob.glob(os.path.join(dcn_dir, 'deform_pool_cuda.*.so')) - success_message = "DCN CUDA operator is compiled successfully at {}.".format(os.path.abspath(os.path.join(parent_dir,'DBNet'))) - print_success(success_message, log_path) + dcn_dir = os.path.join(parent_dir, "DBNet", "assets", "ops", "dcn") + # Just to be safe, check explicitly. + if cpu_or_cuda == "cpu": + conv_cpu_exist = glob.glob( + os.path.join(dcn_dir, "deform_conv_cpu.*.so") + ) or glob.glob(os.path.join(dcn_dir, "deform_conv_cpu.*.pyd")) + pool_cpu_exist = glob.glob( + os.path.join(dcn_dir, "deform_pool_cpu.*.so") + ) or glob.glob(os.path.join(dcn_dir, "deform_pool_cpu.*.pyd")) + return conv_cpu_exist and pool_cpu_exist + elif cpu_or_cuda == "cuda": + conv_cuda_exist = glob.glob( + os.path.join(dcn_dir, "deform_conv_cuda.*.so") + ) or glob.glob(os.path.join(dcn_dir, "deform_conv_cuda.*.pyd")) + pool_cuda_exist = glob.glob( + os.path.join(dcn_dir, "deform_pool_cuda.*.so") + ) or glob.glob(os.path.join(dcn_dir, "deform_pool_cuda.*.pyd")) return conv_cuda_exist and pool_cuda_exist else: raise ValueError("'cpu_or_cuda' must be either 'cpu' or 'cuda'") - + + def main(): cwd = os.getcwd() parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - log_path = os.path.join(parent_dir,'DBNet', 'log.txt') + log_path = os.path.join(parent_dir, "DBNet", "log.txt") try: + if sys.platform == "win32": + os.environ["CXX"] = os.environ["CXX_"] print("Compiling DCN operators...") - os.chdir(os.path.join(parent_dir,'DBNet','assets','ops','dcn')) + os.chdir(os.path.join(parent_dir, "DBNet", "assets", "ops", "dcn")) result = subprocess.run( - "python setup.py build_ext --inplace", shell=True, capture_output = True + ["python", "setup.py", "build_ext", "--inplace"], capture_output=True ) if result.returncode == 0: - os.chdir(os.path.join(parent_dir, 'DBNet')) - if validate_compilation(parent_dir, log_path, 'cpu'): - result = subprocess.run( - "touch dcn_cpu_compiling_success", shell=True, capture_output = True + os.chdir(os.path.join(parent_dir, "DBNet")) + if validate_compilation(parent_dir, log_path, "cpu"): + success_message = ( + "DCN CPU operator is compiled successfully at {}.".format( + os.path.abspath(os.path.join(parent_dir, "DBNet")) + ) ) - if validate_compilation(parent_dir, log_path, 'cuda'): - result = subprocess.run( - "touch dcn_cuda_compiling_success", shell=True, capture_output = True + print_success(success_message, log_path) + with open("dcn_cpu_compiling_success", "w+") as fp: + fp.write("") + if validate_compilation(parent_dir, log_path, "cuda"): + success_message = ( + "DCN CUDA operator is compiled successfully at {}.".format( + os.path.abspath(os.path.join(parent_dir, "DBNet")) + ) ) + print_success(success_message, log_path) + with open("dcn_cuda_compiling_success", "w+") as fp: + fp.write("") + success_message = "DCN operator is compiled successfully at {}.".format( + os.path.abspath(os.path.join(parent_dir, "DBNet")) + ) + print_success(success_message, log_path) else: print(result.__dict__) print_error([result.stdout, result.stderr], log_path) @@ -79,6 +118,6 @@ def main(): finally: os.chdir(cwd) -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() diff --git a/environment.yml b/environment.yml new file mode 100644 index 000000000..c2cf2d67b --- /dev/null +++ b/environment.yml @@ -0,0 +1,120 @@ +channels: + - pytorch + - defaults +dependencies: + - _libgcc_mutex=0.1=main + - _openmp_mutex=5.1=1_gnu + - blas=1.0=mkl + - brotlipy=0.7.0=py39h27cfd23_1003 + - bzip2=1.0.8=h7b6447c_0 + - ca-certificates=2022.07.19=h06a4308_0 + - certifi=2022.6.15=py39h06a4308_0 + - cffi=1.15.1=py39h74dc2b5_0 + - charset-normalizer=2.0.4=pyhd3eb1b0_0 + - cryptography=37.0.1=py39h9ce1e76_0 + - cudatoolkit=11.3.1=h2bc3f7f_2 + - ffmpeg=4.3=hf484d3e_0 + - freetype=2.11.0=h70c0345_0 + - giflib=5.2.1=h7b6447c_0 + - gmp=6.2.1=h295c915_3 + - gnutls=3.6.15=he1e5248_0 + - idna=3.3=pyhd3eb1b0_0 + - intel-openmp=2021.4.0=h06a4308_3561 + - jpeg=9e=h7f8727e_0 + - lame=3.100=h7b6447c_0 + - lcms2=2.12=h3be6417_0 + - ld_impl_linux-64=2.38=h1181459_1 + - lerc=3.0=h295c915_0 + - libdeflate=1.8=h7f8727e_5 + - libffi=3.3=he6710b0_2 + - libgcc-ng=11.2.0=h1234567_1 + - libgomp=11.2.0=h1234567_1 + - libiconv=1.16=h7f8727e_2 + - libidn2=2.3.2=h7f8727e_0 + - libpng=1.6.37=hbc83047_0 + - libstdcxx-ng=11.2.0=h1234567_1 + - libtasn1=4.16.0=h27cfd23_0 + - libtiff=4.4.0=hecacb30_0 + - libunistring=0.9.10=h27cfd23_0 + - libwebp=1.2.2=h55f646e_0 + - libwebp-base=1.2.2=h7f8727e_0 + - lz4-c=1.9.3=h295c915_1 + - mkl=2021.4.0=h06a4308_640 + - mkl-service=2.4.0=py39h7f8727e_0 + - mkl_fft=1.3.1=py39hd3c417c_0 + - mkl_random=1.2.2=py39h51133e4_0 + - ncurses=6.3=h5eee18b_3 + - nettle=3.7.3=hbbd107a_1 + - numpy=1.23.1=py39h6c91a56_0 + - numpy-base=1.23.1=py39ha15fc14_0 + - openh264=2.1.1=h4ff587b_0 + - openssl=1.1.1q=h7f8727e_0 + - pillow=9.2.0=py39hace64e9_1 + - pip=22.1.2=py39h06a4308_0 + - pycparser=2.21=pyhd3eb1b0_0 + - pyopenssl=22.0.0=pyhd3eb1b0_0 + - pysocks=1.7.1=py39h06a4308_0 + - python=3.9.13=haa1d7c7_1 + - pytorch=1.12.1=py3.9_cuda11.3_cudnn8.3.2_0 + - pytorch-mutex=1.0=cuda + - readline=8.1.2=h7f8727e_1 + - requests=2.28.1=py39h06a4308_0 + - setuptools=63.4.1=py39h06a4308_0 + - six=1.16.0=pyhd3eb1b0_1 + - sqlite=3.39.2=h5082296_0 + - tk=8.6.12=h1ccaba5_0 + - torchaudio=0.12.1=py39_cu113 + - torchvision=0.13.1=py39_cu113 + - typing_extensions=4.3.0=py39h06a4308_0 + - tzdata=2022c=h04d1e81_0 + - urllib3=1.26.11=py39h06a4308_0 + - wheel=0.37.1=pyhd3eb1b0_0 + - xz=5.2.5=h7f8727e_1 + - zlib=1.2.12=h5eee18b_3 + - zstd=1.5.2=ha4553b6_0 + - pip: + - asttokens==2.0.8 + - backcall==0.2.0 + - cfgv==3.3.1 + - decorator==5.1.1 + - distlib==0.3.6 + - executing==1.0.0 + - filelock==3.8.0 + - identify==2.5.5 + - imageio==2.21.3 + - ipython==8.5.0 + - jedi==0.18.1 + - matplotlib-inline==0.1.6 + - networkx==2.8.6 + - ninja==1.10.2.3 + - nodeenv==1.7.0 + - opencv-python-headless==4.5.4.60 + - packaging==21.3 + - parso==0.8.3 + - pexpect==4.8.0 + - pickleshare==0.7.5 + - platformdirs==2.5.2 + - pluggy==1.0.0 + - pre-commit==2.20.0 + - prompt-toolkit==3.0.31 + - ptyprocess==0.7.0 + - pure-eval==0.2.2 + - py==1.11.0 + - pyclipper==1.3.0.post3 + - pygments==2.13.0 + - pyparsing==3.0.9 + - python-bidi==0.4.2 + - pywavelets==1.3.0 + - pyyaml==6.0 + - scikit-image==0.19.3 + - scipy==1.9.1 + - shapely==1.8.4 + - stack-data==0.5.0 + - tifffile==2022.8.12 + - toml==0.10.2 + - tomli==2.0.1 + - tox==3.26.0 + - tox-conda==0.9.2 + - traitlets==5.4.0 + - virtualenv==20.16.5 + - wcwidth==0.2.5 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..89a5bedf5 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,9 @@ +[build-system] +# AVOID CHANGING REQUIRES: IT WILL BE UPDATED BY PYSCAFFOLD! +requires = ["setuptools>=46.1.0", "setuptools_scm[toml]>=5"] +build-backend = "setuptools.build_meta" + +[tool.setuptools_scm] +# For smarter version schemes and other configuration options, +# check out https://github.com/pypa/setuptools_scm +version_scheme = "no-guess-dev" diff --git a/requirements.txt b/requirements.txt index 9a62026ed..4add72776 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,52 @@ -torch -torchvision>=0.5 -opencv-python-headless<=4.5.4.60 -scipy -numpy -Pillow -scikit-image -python-bidi -PyYAML -Shapely -pyclipper -ninja +asttokens==2.0.8 +backcall==0.2.0 +brotlipy==0.7.0 +cfgv==3.3.1 +decorator==5.1.1 +distlib==0.3.6 +executing==1.0.0 +filelock==3.8.0 +identify==2.5.5 +imageio==2.21.3 +ipython==8.5.0 +jedi==0.18.1 +matplotlib-inline==0.1.6 +mkl-fft==1.3.1 +mkl-service==2.4.0 +networkx==2.8.6 +ninja==1.10.2.3 +nodeenv==1.7.0 +opencv-python-headless==4.5.4.60 +packaging==21.3 +parso==0.8.3 +pexpect==4.8.0 +pickleshare==0.7.5 +Pillow==9.2.0 +platformdirs==2.5.2 +pluggy==1.0.0 +pre-commit==2.20.0 +prompt-toolkit==3.0.31 +ptyprocess==0.7.0 +pure-eval==0.2.2 +py==1.11.0 +pyclipper==1.3.0.post3 +Pygments==2.13.0 +pyparsing==3.0.9 +python-bidi==0.4.2 +PyWavelets==1.3.0 +PyYAML==6.0 +scikit-image==0.19.3 +scipy==1.9.1 +Shapely==1.8.4 +stack-data==0.5.0 +tifffile==2022.8.12 +toml==0.10.2 +tomli==2.0.1 +torch==1.12.1 +torchaudio==0.12.1 +torchvision==0.13.1 +tox==3.26.0 +tox-conda==0.9.2 +traitlets==5.4.0 +virtualenv==20.16.5 +wcwidth==0.2.5 diff --git a/setup.cfg b/setup.cfg index 08aedd7e6..b81a902c7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,176 @@ -[metadata] -description_file = README.md +# This file is used to configure your project. +# Read more about the various options under: +# https://setuptools.pypa.io/en/latest/userguide/declarative_config.html +# https://setuptools.pypa.io/en/latest/references/keywords.html + +[metadata] +name = easyocr +description = End-to-End Multi-Lingual Optical Character Recognition (OCR) Solution +author = Rakpong Kittinaradorn +author_email = r.kittinaradorn@gmail.com +license = Apache License 2.0 +license_files = LICENSE +long_description = file: README.md +long_description_content_type = text/markdown; charset=UTF-8 +url = https://github.com/jaidedai/easyocr +# Add here related links, for example: +project_urls = + # Documentation = + Source = https://github.com/JaidedAI/EasyOCR + Changelog = https://github.com/JaidedAI/EasyOCR/blob/master/releasenotes.md + Tracker = https://github.com/JaidedAI/EasyOCR/issues + Download = https://pypi.org/project/easyocr/#files +keywords = + easyocr + ocr + optical + character + recognition + deep + learning + neural + network + +# Change if running only on Windows, Mac or Linux (comma-separated) +platforms = any + +# Add here all kinds of additional classifiers as defined under +# https://pypi.org/classifiers/ +classifiers = + Development Status :: 5 - Production/Stable + Programming Language :: Python + + +[options] +zip_safe = False +packages = find_namespace: +include_package_data = True +package_dir = + =. + # TODO: =src + +# Require a min/specific Python version (comma-separated conditions) +python_requires = >=3.7 + +# Add here dependencies of your project (line-separated), e.g. requests>=2.2,<3.0. +# Version specifiers like >=2.2,<3.0 avoid problems due to API changes in +# new major versions. This works if the required packages follow Semantic Versioning. +# For more information, check out https://semver.org/. +install_requires = + importlib-metadata; python_version<"3.8" + typing_extensions; python_version<"3.10" + torch + torchvision>=0.5 + opencv-python-headless<=4.5.4.60 + scipy + numpy + Pillow + scikit-image + python-bidi + PyYAML + Shapely + pyclipper + ninja + + +[options.packages.find] +# TODO: src +where = . +exclude = + tests + +[options.package_data] +easyocr = model/*, character/*.txt, dict/*, scripts/compile_dbnet_dcn.py, DBNet/*, DBNet/configs/*, DBNet/assets/ops/dcn/*.pyd + +[options.exclude_package_data] +easyocr = DBNet/assets/ops/dcn/build/* + +[options.extras_require] +# Add here additional requirements for extra features, to install with: +# `pip install easyocr[PDF]` like: +# PDF = ReportLab; RXP + +# Add here test requirements (semicolon/line-separated) +testing = + setuptools + pytest + pytest-cov + typing_extensions + +[options.entry_points] +# Add here console scripts: +console_scripts = + easyocr = easyocr.cli:main + +[tool:pytest] +# Specify command line options as you would do when invoking pytest directly. +# e.g. --cov-report html (or xml) for html/xml output or --junitxml junit.xml +# in order to write a coverage file that can be read by Jenkins. +# CAUTION: --cov flags may prohibit setting breakpoints while debugging. +# Comment those flags to avoid this pytest issue. +addopts = + --cov easyocr --cov-report term-missing + --verbose +norecursedirs = + dist + build + .tox +testpaths = tests +# Use pytest markers to select/deselect specific tests +# markers = +# slow: mark tests as slow (deselect with '-m "not slow"') +# system: mark end-to-end system tests + +[devpi:upload] +# Options for the devpi: PyPI server and packaging tool +# VCS export must be deactivated since we are using setuptools-scm +no_vcs = 1 +formats = bdist_wheel + +[flake8] +# Some sane defaults for the code style checker flake8 +extend_ignore = E203, W503 +# ^ Black-compatible +# E203 and W503 have edge cases handled by black +exclude = + .tox + build + dist + .eggs + docs/conf.py +strictness = long +show-source = True +max-line-length = 100 +max-complexity = 6 +# TODO: numpy, google, sphinx? +docstring-style = sphinx +ignore = D100, D104, D401, DAR103, DAR203, E800, N802, Q000, RST303, RST304, W504, WPS326, WPS402, WPS433, WPS436 +per-file-ignores = + # WPS421: Found wrong function call: print + setup.py: WPS421 + # WPS412: Found `__init__.py` module with logic + # WPS420: Found wrong keywork: del + # WPS440: Found block variables overlap: PackageNotFoundError, version + src/command_based_framework/__init__.py: WPS412, WPS420, WPS440 + +[isort] +include_trailing_comma = True +use_parentheses = True +multi_line_output = 3 +line_length = 100 + +[mypy] +allow_redefinition = False +check_untyped_defs = True +ignore_errors = False +ignore_missing_imports = True +implicit_reexport = False +local_partial_types = True +strict_optional = True +strict_equality = True +no_implicit_optional = True +warn_unused_ignores = True +warn_redundant_casts = True +warn_unused_configs = True +warn_unreachable = True +warn_no_return = True diff --git a/setup.py b/setup.py index 09b703c14..77fa0a538 100644 --- a/setup.py +++ b/setup.py @@ -1,35 +1,13 @@ -""" -End-to-End Multi-Lingual Optical Character Recognition (OCR) Solution -""" -from io import open from setuptools import setup -with open('requirements.txt', encoding="utf-8-sig") as f: - requirements = f.readlines() - -def readme(): - with open('README.md', encoding="utf-8-sig") as f: - README = f.read() - return README - -setup( - name='easyocr', - packages=['easyocr'], - include_package_data=True, - version='1.6.2', - install_requires=requirements, - entry_points={"console_scripts": ["easyocr= easyocr.cli:main"]}, - license='Apache License 2.0', - description='End-to-End Multi-Lingual Optical Character Recognition (OCR) Solution', - long_description=readme(), - long_description_content_type="text/markdown", - author='Rakpong Kittinaradorn', - author_email='r.kittinaradorn@gmail.com', - url='https://github.com/jaidedai/easyocr', - download_url='https://github.com/jaidedai/easyocr.git', - keywords=['ocr optical character recognition deep learning neural network'], - classifiers=[ - 'Development Status :: 5 - Production/Stable' - ], - -) +if __name__ == "__main__": + try: + setup(use_scm_version={"version_scheme": "no-guess-dev"}) + except Exception: + print( + "\n\nAn error occurred while building the project, " + + "please ensure you have the most updated version of setuptools, " + + "setuptools_scm and wheel with:\n" + + " pip install -U setuptools setuptools_scm wheel\n\n", + ) + raise diff --git a/tox.ini b/tox.ini new file mode 100644 index 000000000..c012ae8de --- /dev/null +++ b/tox.ini @@ -0,0 +1,114 @@ +# Tox configuration file +# Read more under https://tox.wiki/ + +[tox] +minversion = 3.24 +envlist = py{37,38,39,310} +isolated_build = True + + +[testenv] +description = Invoke pytest to run automated tests +setenv = + TOXINIDIR = {toxinidir} +passenv = + HOME + SETUPTOOLS_* +extras = + testing +commands = + pytest {posargs} + + +# To run `tox -e lint` you need to make sure you have a +# `.pre-commit-config.yaml` file. See https://pre-commit.com +[testenv:lint] +description = + Perform static analysis and style checks +skip_install = True +deps = pre-commit +passenv = + HOMEPATH + PROGRAMDATA + SETUPTOOLS_* +commands = + pre-commit run + + +[testenv:{build,clean,cleanall}] +description = + build: Build the package in isolation according to PEP517, see https://github.com/pypa/build + clean: Remove old distribution files and temporary build artifacts (./build and ./dist) + cleanall: Remove old distribution files and temporary build artifacts including DBNet binaries +# https://setuptools.pypa.io/en/stable/build_meta.html#how-to-use-it +skip_install = True +changedir = {toxinidir} +deps = + build: build[virtualenv] + build: setuptools_scm[toml] + build: -rrequirements.txt +conda_deps = + build: pytorch + build: torchvision + build: torchaudio + build: cudatoolkit=11.3 + build: cudatoolkit-dev + build: ninja + build: cxx-compiler=1.3.0 +conda_channels = + pytorch + conda-forge +passenv = + SETUPTOOLS_* + VSINSTALLDIR +setenv = + CXX_ = {env:CXX} +commands = + clean,cleanall: python -c 'import shutil; [shutil.rmtree(p, True) for p in ("build", "dist", "docs/_build", "easyocr/DBNet/assets/ops/dcn/build")]' + clean,cleanall: python -c 'import pathlib, shutil; [shutil.rmtree(p, True) for p in pathlib.Path(".").glob("*.egg-info")]' + cleanall: python -c 'import pathlib; [p.unlink(missing_ok=True) for p in pathlib.Path("easyocr/DBNet/assets/ops/dcn").glob("*.pyd")]' + cleanall: python -c 'import pathlib; pathlib.Path("easyocr/DBNet/dcn_compiling_success").unlink(missing_ok=True); pathlib.Path("easyocr/DBNet/log.txt").unlink(missing_ok=True)' + build: python easyocr/scripts/compile_dbnet_dcn.py + build: python -m build --no-isolation {posargs} + +[testenv:{docs,doctests,linkcheck,autodocs}] +description = + docs: Invoke sphinx-build to build the docs + doctests: Invoke sphinx-build to run doctests + linkcheck: Check for broken links in the documentation + autodocs: Launch a local webserver to hot reload doc changes +passenv = + SETUPTOOLS_* +setenv = + DOCSDIR = {toxinidir}/docs + BUILDDIR = {toxinidir}/docs/_build + docs: BUILD = html + doctests: BUILD = doctest + linkcheck: BUILD = linkcheck +deps = + -r {toxinidir}/docs/requirements.txt + autodocs: sphinx-autobuild + # ^ requirements.txt shared with Read The Docs +commands = + docs: sphinx-build --color -b {env:BUILD} -d "{env:BUILDDIR}/doctrees" "{env:DOCSDIR}" "{env:BUILDDIR}/{env:BUILD}" {posargs} + doctests: sphinx-build --color -b {env:BUILD} -d "{env:BUILDDIR}/doctrees" "{env:DOCSDIR}" "{env:BUILDDIR}/{env:BUILD}" {posargs} + linkcheck: sphinx-build --color -b {env:BUILD} -d "{env:BUILDDIR}/doctrees" "{env:DOCSDIR}" "{env:BUILDDIR}/{env:BUILD}" {posargs} + autodocs: sphinx-autobuild docs docs/_build/html + +[testenv:publish] +description = + Publish the package you have been developing to a package index server. + By default, it uses testpypi. If you really want to publish your package + to be publicly accessible in PyPI, use the `-- --repository pypi` option. +skip_install = True +changedir = {toxinidir} +passenv = + # See: https://twine.readthedocs.io/en/latest/ + TWINE_PASSWORD + TWINE_REPOSITORY +setenv = + TWINE_USERNAME = {env:TWINE_USERNAME:__token__} +deps = twine +commands = + python -m twine check dist/* + python -m twine upload {posargs:--repository {env:TWINE_REPOSITORY:testpypi}} dist/*