diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-12-23 18:53:19 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-12-23 18:53:19 +0000 |
commit | 38d39f536e5a6cff5a218c5dc16994d6eee087e9 (patch) | |
tree | 151b916ddadb35113bac2cd954d99ea26ec64cc0 | |
parent | Updating source url in copyright. (diff) | |
download | terminaltables-38d39f536e5a6cff5a218c5dc16994d6eee087e9.tar.xz terminaltables-38d39f536e5a6cff5a218c5dc16994d6eee087e9.zip |
Merging upstream version 4.0.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
62 files changed, 4198 insertions, 2421 deletions
diff --git a/.converagerc b/.converagerc new file mode 100644 index 0000000..98306ea --- /dev/null +++ b/.converagerc @@ -0,0 +1,25 @@ +[run] +branch = True + +[report] +; Regexes for lines to exclude from consideration +exclude_also = + ; 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__.: + + ; Don't complain about abstract methods, they aren't run: + @(abc\.)?abstractmethod + +ignore_errors = True + +[html] +directory = coverage_html_report
\ No newline at end of file diff --git a/.env_example b/.env_example index 0048805..47c32b4 100644 --- a/.env_example +++ b/.env_example @@ -1,3 +1,4 @@ export PYTHONIOENCODING=utf-8 export LC_ALL=en_US.UTF-8 export LANG=en_US.UTF-8 +export FORCE_COLOR=true
\ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..938602c --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,57 @@ +name: Build and Test + +on: [ push ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.11' + # cache: 'pipenv' # caching pipenv dependencies + - name: Install poetry and pipx + run: | + pip install poetry && pip install pipx + + - name: Install global dependencies + run: | + pipx install isort && pipx install black && pipx install bandit && \ + pipx install pylint && pipx install pre-commit && pipx install poetry + + - name: Install Dependencies + run: poetry install --with dev + + - name: Run Makefile + run: make check + - name: Run make + run: poetry run make publish + - name: Upload Package + uses: actions/upload-artifact@v3.1.2 + with: + name: packages + path: dist/ + if-no-files-found: error + retention-days: 1 + + + pypi-publish: + name: Upload release to PyPI + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/terminaltables3 + permissions: + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing + # if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + steps: + - name: Get packages + uses: actions/download-artifact@v3.0.2 + with: + name: packages + path: dist/ + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + needs: build
\ No newline at end of file @@ -94,3 +94,5 @@ test*.png .idea/ requirements*.txt .DS_Store + +.build_history/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..37c4248 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,95 @@ +--- +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + # each hook takes about 1s to run. These are expensive-ish checks + - id: check-added-large-files + - id: check-yaml + - id: check-builtin-literals + - id: check-byte-order-marker + - id: check-case-conflict + - id: check-merge-conflict + - id: check-symlinks + - id: check-toml + - id: debug-statements + - id: detect-private-key + - id: fix-encoding-pragma + args: [ --remove ] + - id: forbid-new-submodules + - repo: https://github.com/asottile/pyupgrade + rev: v3.16.0 + hooks: + - id: pyupgrade + args: [ --py37-plus ] + # Buggy? Reports no files change, "FAILURE" + # - repo: https://github.com/tox-dev/pyproject-fmt + # rev: "0.4.1" + # hooks: + # - id: pyproject-fmt + # works for me, don't know why it is complaining + # - repo: https://github.com/abravalheri/validate-pyproject + # rev: v0.10.1 + # hooks: + # - id: validate-pyproject + - repo: https://github.com/myint/autoflake + rev: v2.3.1 + hooks: + - id: autoflake + args: + - --in-place + - --recursive + - --expand-star-imports + - --remove-all-unused-imports + - --remove-duplicate-keys + - --remove-unused-variables +# black is conflicting with something else +# - repo: https://github.com/psf/black +# rev: 23.12.1 +# hooks: +# - id: black +# language_version: python3.10 + # - repo: https://github.com/pycqa/isort + # rev: 5.11.4 + # hooks: + # - id: isort + # name: isort + # args: + # - --profile black + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: v0.5.2 + hooks: + - id: ruff + exclude: ^dead_code/ + args: [ + "--config", + "pyproject.toml", + "--fix", + ] + - repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.10.0 # Use the ref you want to point at + hooks: + - id: python-use-type-annotations + - id: python-no-eval + - id: python-no-log-warn + - id: text-unicode-replacement-char +# - repo: https://github.com/igorshubovych/markdownlint-cli +# rev: v0.38.0 +# hooks: +# - id: markdownlint +# # '--fix' +# args: [ 'content/**/*.md', '--config', '.markdownlintrc' ] +# - repo: https://github.com/executablebooks/mdformat +# rev: 0.7.17 # Use the ref you want to point at +# hooks: +# - id: mdformat +# # Optionally add plugins +# additional_dependencies: +# - mdformat-gfm +# - mdformat-black +# - repo: https://github.com/adamchainz/blacken-docs +# rev: 1.18.0 +# hooks: +# - id: blacken-docs +# additional_dependencies: +# - black==23.12.1 diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..5a53fb6 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,633 @@ +[MAIN] + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Load and enable all available extensions. Use --list-extensions to see a list +# all available extensions. +#enable-all-extensions= + +# In error mode, checkers without error messages are disabled and for others, +# only the ERROR messages are displayed, and no reports are done by default. +#errors-only= + +# Always return a 0 (non-error) status code, even if lint errors are found. +# This is primarily useful in continuous integration scripts. +#exit-zero= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. +extension-pkg-allow-list= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. (This is an alternative name to extension-pkg-allow-list +# for backward compatibility.) +extension-pkg-whitelist= + +# Return non-zero exit code if any of these messages/categories are detected, +# even if score is above --fail-under value. Syntax same as enable. Messages +# specified are enabled, while categories only check already-enabled messages. +fail-on= + +# Specify a score threshold to be exceeded before program exits with error. +fail-under=10 + +# Interpret the stdin as a python script, whose filename needs to be passed as +# the module_or_package argument. +#from-stdin= + +# Files or directories to be skipped. They should be base names, not paths. +ignore=CVS + +# Add files or directories matching the regex patterns to the ignore-list. The +# regex matches against paths and can be in Posix or Windows format. +ignore-paths= + +# Files or directories matching the regex patterns are skipped. The regex +# matches against base names, not paths. The default value ignores Emacs file +# locks +ignore-patterns=^\.# + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis). It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use. +jobs=1 + +# Control the amount of potential inferred values when inferring a single +# object. This can help the performance when dealing with large functions or +# complex, nested conditions. +limit-inference-results=100 + +# List of plugins (as comma separated values of python module names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Minimum Python version to use for version dependent checks. Will default to +# the version used to run pylint. +py-version=3.10 + +# Discover python modules and packages in the file system subtree. +recursive=no + +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages. +suggestion-mode=yes + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# In verbose mode, extra non-checker-related info will be displayed. +#verbose= + + +[REPORTS] + +# Python expression which should return a score less than or equal to 10. You +# have access to the variables 'fatal', 'error', 'warning', 'refactor', +# 'convention', and 'info' which contain the number of messages in each +# category, as well as 'statement' which is the total number of statements +# analyzed. This score is used by the global evaluation report (RP0004). +evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details. +msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio). You can also give a reporter class, e.g. +# mypackage.mymodule.MyReporterClass. +#output-format= + +# Tells whether to display a full report or only the messages. +reports=no + +# Activate the evaluation score. +score=yes + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, +# UNDEFINED. +confidence=HIGH, + CONTROL_FLOW, + INFERENCE, + INFERENCE_FAILURE, + UNDEFINED + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once). You can also use "--disable=all" to +# disable everything first and then re-enable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +disable=raw-checker-failed, + bad-inline-option, + locally-disabled, + file-ignored, + suppressed-message, + useless-suppression, + deprecated-pragma, + use-symbolic-message-instead, + + # can't care + fixme, + too-many-lines, + too-many-branches, + import-error, + too-many-statements, + useless-import-alias, + too-many-locals, + consider-using-from-import, # pylint is wrong + duplicate-code, + too-many-arguments, + logging-fstring-interpolation, + too-many-instance-attributes, + too-many-return-statements, + unnecessary-direct-lambda-call, + too-few-public-methods, + logging-not-lazy, + line-too-long, + too-many-public-methods, + + # backwards compatibility forever! + consider-using-f-string, + super-with-arguments + + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable=c-extension-no-member + + +[BASIC] + +# Naming style matching correct argument names. +argument-naming-style=snake_case + +# Regular expression matching correct argument names. Overrides argument- +# naming-style. If left empty, argument names will be checked with the set +# naming style. +#argument-rgx= + +# Naming style matching correct attribute names. +attr-naming-style=snake_case + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style. If left empty, attribute names will be checked with the set naming +# style. +#attr-rgx= + +# Bad variable names which should always be refused, separated by a comma. +bad-names=foo, + bar, + baz, + toto, + tutu, + tata + +# Bad variable names regexes, separated by a comma. If names match any regex, +# they will always be refused +bad-names-rgxs= + +# Naming style matching correct class attribute names. +class-attribute-naming-style=any + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style. If left empty, class attribute names will be checked +# with the set naming style. +#class-attribute-rgx= + +# Naming style matching correct class constant names. +class-const-naming-style=UPPER_CASE + +# Regular expression matching correct class constant names. Overrides class- +# const-naming-style. If left empty, class constant names will be checked with +# the set naming style. +#class-const-rgx= + +# Naming style matching correct class names. +class-naming-style=PascalCase + +# Regular expression matching correct class names. Overrides class-naming- +# style. If left empty, class names will be checked with the set naming style. +#class-rgx= + +# Naming style matching correct constant names. +const-naming-style=UPPER_CASE + +# Regular expression matching correct constant names. Overrides const-naming- +# style. If left empty, constant names will be checked with the set naming +# style. +#const-rgx= + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming style matching correct function names. +function-naming-style=snake_case + +# Regular expression matching correct function names. Overrides function- +# naming-style. If left empty, function names will be checked with the set +# naming style. +#function-rgx= + +# Good variable names which should always be accepted, separated by a comma. +good-names=i, + j, + k, + ex, + Run, + _ + +# Good variable names regexes, separated by a comma. If names match any regex, +# they will always be accepted +good-names-rgxs= + +# Include a hint for the correct naming format with invalid-name. +include-naming-hint=no + +# Naming style matching correct inline iteration names. +inlinevar-naming-style=any + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style. If left empty, inline iteration names will be checked +# with the set naming style. +#inlinevar-rgx= + +# Naming style matching correct method names. +method-naming-style=snake_case + +# Regular expression matching correct method names. Overrides method-naming- +# style. If left empty, method names will be checked with the set naming style. +#method-rgx= + +# Naming style matching correct module names. +module-naming-style=snake_case + +# Regular expression matching correct module names. Overrides module-naming- +# style. If left empty, module names will be checked with the set naming style. +#module-rgx= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +# These decorators are taken in consideration only for invalid-name. +property-classes=abc.abstractproperty + +# Regular expression matching correct type variable names. If left empty, type +# variable names will be checked with the set naming style. +#typevar-rgx= + +# Naming style matching correct variable names. +variable-naming-style=snake_case + +# Regular expression matching correct variable names. Overrides variable- +# naming-style. If left empty, variable names will be checked with the set +# naming style. +#variable-rgx= + + +[CLASSES] + +# Warn about protected attribute access inside special methods +check-protected-access-in-special-methods=no + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp, + __post_init__ + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict, + _fields, + _replace, + _source, + _make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=cls + + +[DESIGN] + +# List of regular expressions of class ancestor names to ignore when counting +# public methods (see R0903) +exclude-too-few-public-methods= + +# List of qualified class names to ignore when counting class parents (see +# R0901) +ignored-parents= + +# Maximum number of arguments for function / method. +max-args=5 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Maximum number of boolean expressions in an if statement (see R0916). +max-bool-expr=5 + +# Maximum number of branch for function / method body. +max-branches=12 + +# Maximum number of locals for function / method body. +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body. +max-returns=6 + +# Maximum number of statements in function / method body. +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when caught. +overgeneral-exceptions=builtins.BaseException, + builtins.Exception + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )?<?https?://\S+>?$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=120 + +# Maximum number of lines in a module. +max-module-lines=1000 + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[IMPORTS] + +# List of modules that can be imported at any level, not just the top level +# one. +allow-any-import-level= + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules= + +# Output a graph (.gv or any supported image format) of external dependencies +# to the given file (report RP0402 must not be disabled). +ext-import-graph= + +# Output a graph (.gv or any supported image format) of all (i.e. internal and +# external) dependencies to the given file (report RP0402 must not be +# disabled). +import-graph= + +# Output a graph (.gv or any supported image format) of internal dependencies +# to the given file (report RP0402 must not be disabled). +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + +# Couples of modules and preferred modules, separated by a comma. +preferred-modules= + + +[LOGGING] + +# The type of string formatting that logging methods do. `old` means using % +# formatting, `new` is for `{}` formatting. +logging-format-style=old + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME, + XXX, + TODO + +# Regular expression of note tags to take in consideration. +notes-rgx= + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +# Complete name of functions that never returns. When checking for +# inconsistent-return-statements if a never returning function is called then +# it will be considered as an explicit return statement and no message will be +# printed. +never-returning-functions=sys.exit,argparse.parse_error + + +[SIMILARITIES] + +# Comments are removed from the similarity computation +ignore-comments=yes + +# Docstrings are removed from the similarity computation +ignore-docstrings=yes + +# Imports are removed from the similarity computation +ignore-imports=yes + +# Signatures are removed from the similarity computation +ignore-signatures=yes + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 + +# Spelling dictionary name. Available dictionaries: none. To make it work, +# install the 'python-enchant' package. +spelling-dict= + +# List of comma separated words that should be considered directives if they +# appear at the beginning of a comment and should not be checked. +spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains the private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to the private dictionary (see the +# --spelling-private-dict-file option) instead of raising a message. +spelling-store-unknown-words=no + + +[STRING] + +# This flag controls whether inconsistent-quotes generates a warning when the +# character used as a quote delimiter is used inconsistently within a module. +check-quote-consistency=no + +# This flag controls whether the implicit-str-concat should generate a warning +# on implicit string concatenation in sequences defined over several lines. +check-str-concat-over-line-jumps=no + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of symbolic message names to ignore for Mixin members. +ignored-checks-for-mixins=no-member, + not-async-context-manager, + not-context-manager, + attribute-defined-outside-init + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + +# Regex pattern to define which classes are considered mixins. +mixin-class-rgx=.*[Mm]ixin + +# List of decorators that change the signature of a decorated function. +signature-mutators= + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid defining new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of names allowed to shadow builtins +allowed-redefined-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_, + _cb + +# A regular expression matching the name of dummy variables (i.e. expected to +# not be used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io diff --git a/CHANGELOG.md b/CHANGELOG.md index e8706fe..fa06ec6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,20 @@ Changelog This project adheres to `Semantic Versioning <http://semver.org/>`_. -3.1.7 - 2016-12-7 +4.0.0 - 2024-1-1 +------------------ + +Changed + * New namespace and package name: terminaltables3 + * No changes made to the License. + +Added + * Started on type annotations + +Removed + * Removed Python 2 support. Possibly supports earlier 3.x versions but no longer checking in build script. + +3.1.7 - 2021-1-1 ------------------ Added diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4b57351 --- /dev/null +++ b/Makefile @@ -0,0 +1,104 @@ +# isort . && black . && bandit -r . && pylint && pre-commit run --all-files +# Get changed files + +FILES := $(wildcard **/*.py) + +# if you wrap everything in pipenv run, it runs slower. +ifeq ($(origin VIRTUAL_ENV),undefined) + VENV := poetry run +else + VENV := +endif + + + +clean-pyc: + @echo "Removing compiled files" + @find . -name '*.pyc' -exec rm -f {} + || true + @find . -name '*.pyo' -exec rm -f {} + || true + @find . -name '__pycache__' -exec rm -fr {} + || true + +clean-test: + @echo "Removing coverage data" + @rm -f .coverage || true + @rm -f .coverage.* || true + +clean: clean-pyc clean-test + +# tests can't be expected to pass if dependencies aren't installed. +# tests are often slow and linting is fast, so run tests on linted code. +test: clean .build_history/pylint .build_history/bandit + @echo "Running unit tests" + export FORCE_COLOR=true + # $(VENV) pytest terminaltables3 --doctest-modules + # $(VENV) python -m unittest discover + # not all tests run in all OSs, giving seemingly low coverage. + FORCE_COLOR=true $(VENV) py.test tests --cov=terminaltables3 --cov-report=html --cov-fail-under 75 + +.build_history: + @mkdir -p .build_history + +.build_history/isort: .build_history $(FILES) + @echo "Formatting imports" + $(VENV) isort terminaltables3 + @touch .build_history/isort + +.PHONY: isort +isort: .build_history/isort + +.build_history/black: .build_history .build_history/isort $(FILES) + @echo "Formatting code" + $(VENV) black . --exclude .virtualenv --exclude .tox + @touch .build_history/black + +.PHONY: black +black: .build_history/black + +.build_history/pre-commit: .build_history .build_history/isort .build_history/black + @echo "Pre-commit checks" + $(VENV) pre-commit run --all-files + @touch .build_history/pre-commit + +.PHONY: pre-commit +pre-commit: .build_history/pre-commit + +.build_history/bandit: .build_history $(FILES) + @echo "Security checks" + $(VENV) bandit . + @touch .build_history/bandit + +.PHONY: bandit +bandit: .build_history/bandit + +.PHONY: pylint +.build_history/pylint: .build_history .build_history/isort .build_history/black $(FILES) + @echo "Linting with pylint" + # TODO lint tests + $(VENV) pylint terminaltables3 --fail-under 10 + @touch .build_history/pylint + +.PHONY: ruff +.build_history/ruff: .build_history .build_history/isort .build_history/black $(FILES) + @echo "Linting with ruff" + $(VENV) ruff check terminaltables3 tests --fix + @touch .build_history/ruff + +.PHONY: ruff +ruff: .build_history/ruff + +# for when using -j (jobs, run in parallel) +.NOTPARALLEL: .build_history/isort .build_history/black + +check: test pylint ruff bandit pre-commit + +.PHONY: publish +publish: check + rm -rf dist && poetry build + +.PHONY: build +build: check + rm -rf dist && poetry build + +.PHONY: mypy +mypy: + $(VENV) mypy terminaltables3 --ignore-missing-imports --check-untyped-defs
\ No newline at end of file diff --git a/Pipfile b/Pipfile deleted file mode 100644 index 829b4c6..0000000 --- a/Pipfile +++ /dev/null @@ -1,16 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] - -[dev-packages] -colorama=">=0.3.7" -colorclass=">=2.2.0" -pytest-cov=">=2.4.0" -termcolor = "*" -check-wheel-contents = "*" - -[requires] -python_version = "3.9" @@ -1,11 +1,19 @@ -## terminaltables - # What is it Easily draw tables in terminal/console applications from a list of lists of strings. Supports multi-line rows. -- Python 2.6, 2.7, PyPy, PyPy3, 3.3, 3.4, and 3.5+ supported on Linux and OS X. -- Python 2.7, 3.3, 3.4, and 3.5+ supported on Windows (both 32 and 64 bit versions of Python). +Tested on Python 3.8+ + +**This is a fork of the terminaltables project. Which is archived and unmaintained. This library is in a new namespace +but should otherwise be a drop in replacement. Maintaining goals consist of maintaining ecosystem compatibility, type +annotations and responding to community pull requests.** + +To Upgrade +========== +Replace all instances of `terminaltables` with `terminaltables3` in your code. If other libraries depend on `terminaltables` +in your venv they will not conflict because it is a new namespace. + +As of right now, the documentation as the robpol86 version. 📖 Full documentation: https://robpol86.github.io/terminaltables @@ -15,19 +23,19 @@ Quickstart Install: ```bash -pip install terminaltables +pip install terminaltables3 ``` Usage: ```python -from terminaltables import AsciiTable +from terminaltables3 import AsciiTable table_data = [ - ['Heading1', 'Heading2'], - ['row1 column1', 'row1 column2'], - ['row2 column1', 'row2 column2'], - ['row3 column1', 'row3 column2'] + ["Heading1", "Heading2"], + ["row1 column1", "row1 column2"], + ["row2 column1", "row2 column2"], + ["row3 column1", "row3 column2"], ] table = AsciiTable(table_data) print @@ -54,4 +62,4 @@ Source code for examples: - [example2.py](https://github.com/matthewdeanmartin/terminaltables/blob/master/example2.py) - [example3.py](https://github.com/matthewdeanmartin/terminaltables/blob/master/example3.py) -[Change Log](https://github.com/matthewdeanmartin/terminaltables/blob/master/CHANGELOG.md)
\ No newline at end of file +[Change Log](https://github.com/matthewdeanmartin/terminaltables/blob/master/CHANGELOG.md) diff --git a/dead_code/.travis.yml b/dead_code/.travis.yml deleted file mode 100644 index 02cf421..0000000 --- a/dead_code/.travis.yml +++ /dev/null @@ -1,49 +0,0 @@ -# Configure. -language: python -python: - - 3.5 - - 3.4 - - 3.3 - - pypy3 - - pypy - - 2.7 - - 2.6 -sudo: false - -# Environment and matrix. -env: TOX_ENV=py -matrix: - include: - - python: 3.5 - env: TOX_ENV=lint - after_success: [] - - python: 3.5 - env: TOX_ENV=docs - after_success: - - eval "$(ssh-agent -s)"; touch docs/key; chmod 0600 $_ - - openssl aes-256-cbc -d -K "$encrypted_c89fed6a587d_key" -iv "$encrypted_c89fed6a587d_iv" -out docs/key - < docs/key.enc && ssh-add $_ - - git config --global user.email "builds@travis-ci.com" - - git config --global user.name "Travis CI" - - git remote set-url --push origin "git@github.com:$TRAVIS_REPO_SLUG" - - export ${!TRAVIS*} - - tox -e docsV - -# Run. -install: pip install tox -script: tox -e $TOX_ENV -after_success: - - bash <(curl -s https://codecov.io/bash) - -# Deploy. -deploy: - provider: pypi - user: Robpol86 - password: - secure: - "aj+Hl25+NbtmKpHcqxxNJhaMmawgzEPdLX+NwxwAZuTrvUCdiMtYhF9qxN0USHIlXSGDNc\ - 7ua6nNpYPhjRv7j5YM4uLlK+4Fv/iU+iQcVfy89BS4vlXzUoje6nLIhogsxytb+FjdGZ0PK\ - JzzxfYr0relUjui/gPYmTQoZ1IiT8A=" - on: - condition: $TRAVIS_PYTHON_VERSION = 3.4 - tags: true diff --git a/dead_code/README.rst b/dead_code/README.rst deleted file mode 100644 index fe9044f..0000000 --- a/dead_code/README.rst +++ /dev/null @@ -1,162 +0,0 @@ -============== -terminaltables -============== - -Easily draw tables in terminal/console applications from a list of lists of strings. Supports multi-line rows. - -* Python 2.6, 2.7, PyPy, PyPy3, 3.3, 3.4, and 3.5 supported on Linux and OS X. -* Python 2.7, 3.3, 3.4, and 3.5 supported on Windows (both 32 and 64 bit versions of Python). - -📖 Full documentation: https://robpol86.github.io/terminaltables - -.. image:: https://img.shields.io/appveyor/ci/Robpol86/terminaltables/master.svg?style=flat-square&label=AppVeyor%20CI - :target: https://ci.appveyor.com/project/Robpol86/terminaltables - :alt: Build Status Windows - -.. image:: https://img.shields.io/travis/Robpol86/terminaltables/master.svg?style=flat-square&label=Travis%20CI - :target: https://travis-ci.org/Robpol86/terminaltables - :alt: Build Status - -.. image:: https://img.shields.io/codecov/c/github/Robpol86/terminaltables/master.svg?style=flat-square&label=Codecov - :target: https://codecov.io/gh/Robpol86/terminaltables - :alt: Coverage Status - -.. image:: https://img.shields.io/pypi/v/terminaltables.svg?style=flat-square&label=Latest - :target: https://pypi.python.org/pypi/terminaltables - :alt: Latest Version - -Quickstart -========== - -Install: - -.. code:: bash - - pip install terminaltables - -Usage: - -.. code:: - - from terminaltables import AsciiTable - table_data = [ - ['Heading1', 'Heading2'], - ['row1 column1', 'row1 column2'], - ['row2 column1', 'row2 column2'], - ['row3 column1', 'row3 column2'] - ] - table = AsciiTable(table_data) - print table.table - +--------------+--------------+ - | Heading1 | Heading2 | - +--------------+--------------+ - | row1 column1 | row1 column2 | - | row2 column1 | row2 column2 | - | row3 column1 | row3 column2 | - +--------------+--------------+ - -Example Implementations -======================= - -.. image:: docs/examples.png?raw=true - :alt: Example Scripts Screenshot - -Source code for examples: `example1.py <https://github.com/Robpol86/terminaltables/blob/master/example1.py>`_, -`example2.py <https://github.com/Robpol86/terminaltables/blob/master/example2.py>`_, and -`example3.py <https://github.com/Robpol86/terminaltables/blob/master/example3.py>`_ - -.. changelog-section-start - -Changelog -========= - -This project adheres to `Semantic Versioning <http://semver.org/>`_. - -3.1.0 - 2016-10-16 ------------------- - -Added - * ``git --porcelain``-like table by liiight: https://github.com/Robpol86/terminaltables/pull/31 - -3.0.0 - 2016-05-30 ------------------- - -Added - * Support for https://pypi.python.org/pypi/colorama - * Support for https://pypi.python.org/pypi/termcolor - * Support for RTL characters (Arabic and Hebrew). - * Support for non-string items in ``table_data`` like integers. - -Changed - * Refactored again, but this time entire project including tests. - -Removed - * ``padded_table_data`` property and ``join_row()``. Moving away from repeated string joining/splitting. - -Fixed - * ``set_terminal_title()`` Unicode handling on Windows. - * https://github.com/Robpol86/terminaltables/issues/18 - * https://github.com/Robpol86/terminaltables/issues/20 - * https://github.com/Robpol86/terminaltables/issues/23 - * https://github.com/Robpol86/terminaltables/issues/26 - -2.1.0 - 2015-11-02 ------------------- - -Added - * GitHub Flavored Markdown table by bcho: https://github.com/Robpol86/terminaltables/pull/12 - * Python 3.5 support (Linux/OS X and Windows). - -2.0.0 - 2015-10-11 ------------------- - -Changed - * Refactored code. No new features. - * Breaking changes: ``UnixTable``/``WindowsTable``/``WindowsTableDouble`` moved. Use ``SingleTable``/``DoubleTable`` - instead. - -1.2.1 - 2015-09-03 ------------------- - -Fixed - * CJK character width fixed by zqqf16 and bcho: https://github.com/Robpol86/terminaltables/pull/9 - -1.2.0 - 2015-05-31 ------------------- - -Added - * Bottom row separator. - -1.1.1 - 2014-11-03 ------------------- - -Fixed - * Python 2.7 64-bit terminal width bug on Windows. - -1.1.0 - 2014-11-02 ------------------- - -Added - * Windows support. - * Double-lined table. - -1.0.2 - 2014-09-18 ------------------- - -Added - * ``table_width`` and ``ok`` properties. - -1.0.1 - 2014-09-12 ------------------- - -Added - * Terminal width/height defaults for testing. - * ``terminaltables.DEFAULT_TERMINAL_WIDTH`` - * ``terminaltables.DEFAULT_TERMINAL_HEIGHT`` - -1.0.0 - 2014-09-11 ------------------- - -* Initial release. - -.. changelog-section-end diff --git a/dead_code/appveyor.yml b/dead_code/appveyor.yml deleted file mode 100644 index 8b7d1c9..0000000 --- a/dead_code/appveyor.yml +++ /dev/null @@ -1,27 +0,0 @@ -# Configure. -environment: - PATH: C:\%PYTHON%;C:\%PYTHON%\Scripts;%PATH% - PYTHON: Python35 - matrix: - - TOX_ENV: lint - - TOX_ENV: py35 - - TOX_ENV: py34 - - TOX_ENV: py33 - - TOX_ENV: py27 - - TOX_ENV: py - PYTHON: Python35-x64 - - TOX_ENV: py - PYTHON: Python34-x64 - - TOX_ENV: py - PYTHON: Python33-x64 - - TOX_ENV: py - PYTHON: Python27-x64 - -# Run. -build_script: pip install tox -test_script: tox -e %TOX_ENV% -on_success: IF %TOX_ENV% NEQ lint pip install codecov & codecov - -# Post. -on_finish: - - FOR %%F IN (test*.png) DO appveyor PushArtifact %%F diff --git a/dead_code/setup.py b/dead_code/setup.py deleted file mode 100644 index a947444..0000000 --- a/dead_code/setup.py +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env python -"""Setup script for the project.""" - -import codecs -import os -import re - -from setuptools import Command, setup - -INSTALL_REQUIRES = [] -LICENSE = 'MIT' -NAME = IMPORT = 'terminaltables' -VERSION = '3.1.0' - - -def readme(path='README.rst'): - """Try to read README.rst or return empty string if failed. - - :param str path: Path to README file. - - :return: File contents. - :rtype: str - """ - path = os.path.realpath(os.path.join(os.path.dirname(__file__), path)) - handle = None - url_prefix = 'https://raw.githubusercontent.com/Robpol86/{name}/v{version}/'.format(name=NAME, version=VERSION) - try: - handle = codecs.open(path, encoding='utf-8') - return handle.read(131072).replace('.. image:: docs', '.. image:: {0}docs'.format(url_prefix)) - except IOError: - return '' - finally: - getattr(handle, 'close', lambda: None)() - - -class CheckVersion(Command): - """Make sure version strings and other metadata match here, in module/package, tox, and other places.""" - - description = 'verify consistent version/etc strings in project' - user_options = [] - - @classmethod - def initialize_options(cls): - """Required by distutils.""" - pass - - @classmethod - def finalize_options(cls): - """Required by distutils.""" - pass - - @classmethod - def run(cls): - """Check variables.""" - project = __import__(IMPORT, fromlist=['']) - for expected, var in [('@Robpol86', '__author__'), (LICENSE, '__license__'), (VERSION, '__version__')]: - if getattr(project, var) != expected: - raise SystemExit('Mismatch: {0}'.format(var)) - # Check changelog. - if not re.compile(r'^%s - \d{4}-\d{2}-\d{2}[\r\n]' % VERSION, re.MULTILINE).search(readme()): - raise SystemExit('Version not found in readme/changelog file.') - # Check tox. - if INSTALL_REQUIRES: - contents = readme('tox.ini') - section = re.compile(r'[\r\n]+install_requires =[\r\n]+(.+?)[\r\n]+\w', re.DOTALL).findall(contents) - if not section: - raise SystemExit('Missing install_requires section in tox.ini.') - in_tox = re.findall(r' ([^=]+)==[\w\d.-]+', section[0]) - if INSTALL_REQUIRES != in_tox: - raise SystemExit('Missing/unordered pinned dependencies in tox.ini.') - - -if __name__ == '__main__': - setup( - author='@Robpol86', - author_email='robpol86@gmail.com', - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Console', - 'Environment :: MacOS X', - 'Environment :: Win32 (MS Windows)', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX :: Linux', - 'Operating System :: POSIX', - 'Programming Language :: Python :: 2.6', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: Implementation :: PyPy', - 'Topic :: Software Development :: Libraries', - 'Topic :: Terminals', - 'Topic :: Text Processing :: Markup', - ], - cmdclass=dict(check_version=CheckVersion), - description='Generate simple tables in terminals from a nested list of strings.', - install_requires=INSTALL_REQUIRES, - keywords='Shell Bash ANSI ASCII terminal tables', - license=LICENSE, - long_description=readme(), - name=NAME, - packages=[IMPORT], - url='https://github.com/Robpol86/' + NAME, - version=VERSION, - zip_safe=True, - ) diff --git a/docs/conf.py b/docs/conf.py index 0e8b9c0..dd96129 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -6,50 +6,55 @@ import time # General configuration. -sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))) -author = '@Robpol86' -copyright = '{}, {}'.format(time.strftime('%Y'), author) -html_last_updated_fmt = '%c {}'.format(time.tzname[time.localtime().tm_isdst]) -master_doc = 'index' -project = __import__('setup').NAME -pygments_style = 'friendly' -release = version = __import__('setup').VERSION -templates_path = ['_templates'] -extensions = list() +sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) +author = "@Robpol86" +copyright = "{}, {}".format(time.strftime("%Y"), author) +html_last_updated_fmt = f"%c {time.tzname[time.localtime().tm_isdst]}" +master_doc = "index" +project = __import__("setup").NAME +pygments_style = "friendly" +release = version = __import__("setup").VERSION +templates_path = ["_templates"] +extensions = [] # Options for HTML output. html_context = dict( - conf_py_path='/docs/', + conf_py_path="/docs/", display_github=True, - github_repo=os.environ.get('TRAVIS_REPO_SLUG', '/' + project).split('/', 1)[1], - github_user=os.environ.get('TRAVIS_REPO_SLUG', 'robpol86/').split('/', 1)[0], - github_version=os.environ.get('TRAVIS_BRANCH', 'master'), - source_suffix='.rst', + github_repo=os.environ.get("TRAVIS_REPO_SLUG", "/" + project).split("/", 1)[1], + github_user=os.environ.get("TRAVIS_REPO_SLUG", "robpol86/").split("/", 1)[0], + github_version=os.environ.get("TRAVIS_BRANCH", "master"), + source_suffix=".rst", ) html_copy_source = False -html_favicon = 'favicon.ico' -html_theme = 'sphinx_rtd_theme' +html_favicon = "favicon.ico" +html_theme = "sphinx_rtd_theme" html_title = project # autodoc -extensions.append('sphinx.ext.autodoc') +extensions.append("sphinx.ext.autodoc") # extlinks. -extensions.append('sphinx.ext.extlinks') -extlinks = {'github': ('https://github.com/robpol86/{0}/blob/v{1}/%s'.format(project, version), '')} +extensions.append("sphinx.ext.extlinks") +extlinks = { + "github": ( + f"https://github.com/robpol86/{project}/blob/v{version}/%s", + "", + ) +} # google analytics -extensions.append('sphinxcontrib.googleanalytics') -googleanalytics_id = 'UA-82627369-1' +extensions.append("sphinxcontrib.googleanalytics") +googleanalytics_id = "UA-82627369-1" # SCVersioning. scv_banner_greatest_tag = True -scv_grm_exclude = ('.gitignore', '.nojekyll', 'README.rst') -scv_overflow = ('-W',) +scv_grm_exclude = (".gitignore", ".nojekyll", "README.rst") +scv_overflow = ("-W",) scv_show_banner = True -scv_sort = ('semver', 'time') +scv_sort = ("semver", "time") diff --git a/example1.py b/example1.py index daf1fbf..4006eac 100755 --- a/example1.py +++ b/example1.py @@ -4,39 +4,38 @@ Just prints sample text and exits. """ -from __future__ import print_function -from terminaltables import AsciiTable, DoubleTable, SingleTable +from terminaltables3 import AsciiTable, DoubleTable, SingleTable TABLE_DATA = ( - ('Platform', 'Years', 'Notes'), - ('Mk5', '2007-2009', 'The Golf Mk5 Variant was\nintroduced in 2007.'), - ('MKVI', '2009-2013', 'Might actually be Mk5.'), + ("Platform", "Years", "Notes"), + ("Mk5", "2007-2009", "The Golf Mk5 Variant was\nintroduced in 2007."), + ("MKVI", "2009-2013", "Might actually be Mk5."), ) def main(): """Main function.""" - title = 'Jetta SportWagen' + title = "Jetta SportWagen" # AsciiTable. table_instance = AsciiTable(TABLE_DATA, title) - table_instance.justify_columns[2] = 'right' + table_instance.justify_columns[2] = "right" print(table_instance.table) print() # SingleTable. table_instance = SingleTable(TABLE_DATA, title) - table_instance.justify_columns[2] = 'right' + table_instance.justify_columns[2] = "right" print(table_instance.table) print() # DoubleTable. table_instance = DoubleTable(TABLE_DATA, title) - table_instance.justify_columns[2] = 'right' + table_instance.justify_columns[2] = "right" print(table_instance.table) print() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/example2.py b/example2.py index 51644f8..b644d62 100755 --- a/example2.py +++ b/example2.py @@ -4,19 +4,21 @@ Just prints sample text and exits. """ -from __future__ import print_function from colorclass import Color, Windows -from terminaltables import SingleTable +from terminaltables3 import SingleTable def table_server_timings(): """Return table string to be printed.""" table_data = [ - [Color('{autogreen}<10ms{/autogreen}'), '192.168.0.100, 192.168.0.101'], - [Color('{autoyellow}10ms <= 100ms{/autoyellow}'), '192.168.0.102, 192.168.0.103'], - [Color('{autored}>100ms{/autored}'), '192.168.0.105'], + [Color("{autogreen}<10ms{/autogreen}"), "192.168.0.100, 192.168.0.101"], + [ + Color("{autoyellow}10ms <= 100ms{/autoyellow}"), + "192.168.0.102, 192.168.0.103", + ], + [Color("{autored}>100ms{/autored}"), "192.168.0.105"], ] table_instance = SingleTable(table_data) table_instance.inner_heading_row_border = False @@ -26,20 +28,32 @@ def table_server_timings(): def table_server_status(): """Return table string to be printed.""" table_data = [ - [Color('Low Space'), Color('{autocyan}Nominal Space{/autocyan}'), Color('Excessive Space')], - [Color('Low Load'), Color('Nominal Load'), Color('{autored}High Load{/autored}')], - [Color('{autocyan}Low Free RAM{/autocyan}'), Color('Nominal Free RAM'), Color('High Free RAM')], + [ + Color("Low Space"), + Color("{autocyan}Nominal Space{/autocyan}"), + Color("Excessive Space"), + ], + [ + Color("Low Load"), + Color("Nominal Load"), + Color("{autored}High Load{/autored}"), + ], + [ + Color("{autocyan}Low Free RAM{/autocyan}"), + Color("Nominal Free RAM"), + Color("High Free RAM"), + ], ] - table_instance = SingleTable(table_data, '192.168.0.105') + table_instance = SingleTable(table_data, "192.168.0.105") table_instance.inner_heading_row_border = False table_instance.inner_row_border = True - table_instance.justify_columns = {0: 'center', 1: 'center', 2: 'center'} + table_instance.justify_columns = {0: "center", 1: "center", 2: "center"} return table_instance.table def table_abcd(): """Return table string to be printed. Two tables on one line.""" - table_instance = SingleTable([['A', 'B'], ['C', 'D']]) + table_instance = SingleTable([["A", "B"], ["C", "D"]]) # Get first table lines. table_instance.outer_border = False @@ -53,16 +67,18 @@ def table_abcd(): # Combine. smallest, largest = sorted([table_inner_borders, table_outer_borders], key=len) - smallest += [''] * (len(largest) - len(smallest)) # Make both same size. - combined = list() + smallest += [""] * (len(largest) - len(smallest)) # Make both same size. + combined = [] for i, row in enumerate(largest): - combined.append(row.ljust(10) + ' ' + smallest[i]) - return '\n'.join(combined) + combined.append(row.ljust(10) + " " + smallest[i]) + return "\n".join(combined) def main(): """Main function.""" - Windows.enable(auto_colors=True, reset_atexit=True) # Does nothing if not on Windows. + Windows.enable( + auto_colors=True, reset_atexit=True + ) # Does nothing if not on Windows. # Server timings. print(table_server_timings()) @@ -77,10 +93,10 @@ def main(): print() # Instructions. - table_instance = SingleTable([['Obey Obey Obey Obey']], 'Instructions') + table_instance = SingleTable([["Obey Obey Obey Obey"]], "Instructions") print(table_instance.table) print() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/example3.py b/example3.py index bec5500..c32d842 100755 --- a/example3.py +++ b/example3.py @@ -4,33 +4,37 @@ Just prints sample text and exits. """ -from __future__ import print_function from textwrap import wrap -from terminaltables import SingleTable +from terminaltables3 import SingleTable -LONG_STRING = ('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore ' - 'et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut ' - 'aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum ' - 'dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui ' - 'officia deserunt mollit anim id est laborum.') +LONG_STRING = ( + "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore " + "et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " + "aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum " + "dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui " + "officia deserunt mollit anim id est laborum." +) def main(): """Main function.""" table_data = [ - ['Long String', ''], # One row. Two columns. Long string will replace this empty string. + [ + "Long String", + "", + ], # One row. Two columns. Long string will replace this empty string. ] table = SingleTable(table_data) # Calculate newlines. max_width = table.column_max_width(1) - wrapped_string = '\n'.join(wrap(LONG_STRING, max_width)) + wrapped_string = "\n".join(wrap(LONG_STRING, max_width)) table.table_data[0][1] = wrapped_string print(table.table) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..b7f1f5e --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1058 @@ +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. + +[[package]] +name = "astroid" +version = "3.2.3" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.2.3-py3-none-any.whl", hash = "sha256:3eae9ea67c11c858cdd2c91337d2e816bd019ac897ca07d7b346ac10105fceb3"}, + {file = "astroid-3.2.3.tar.gz", hash = "sha256:7099b5a60985529d8d46858befa103b82d0d05a5a5e8b816b5303ed96075e1d9"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "bandit" +version = "1.7.9" +description = "Security oriented static analyser for python code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "bandit-1.7.9-py3-none-any.whl", hash = "sha256:52077cb339000f337fb25f7e045995c4ad01511e716e5daac37014b9752de8ec"}, + {file = "bandit-1.7.9.tar.gz", hash = "sha256:7c395a436743018f7be0a4cbb0a4ea9b902b6d87264ddecf8cfdc73b4f78ff61"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} +PyYAML = ">=5.3.1" +rich = "*" +stevedore = ">=1.20.0" + +[package.extras] +baseline = ["GitPython (>=3.1.30)"] +sarif = ["jschema-to-python (>=1.2.3)", "sarif-om (>=1.0.4)"] +test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] +toml = ["tomli (>=1.1.0)"] +yaml = ["PyYAML"] + +[[package]] +name = "black" +version = "24.4.2" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"}, + {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"}, + {file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"}, + {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"}, + {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"}, + {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"}, + {file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"}, + {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"}, + {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"}, + {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"}, + {file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"}, + {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"}, + {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"}, + {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"}, + {file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"}, + {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"}, + {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"}, + {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"}, + {file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"}, + {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"}, + {file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"}, + {file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "cachetools" +version = "5.3.3" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, +] + +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] + +[[package]] +name = "chardet" +version = "5.2.0" +description = "Universal encoding detector for Python 3" +optional = false +python-versions = ">=3.7" +files = [ + {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, + {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "colorclass" +version = "2.2.2" +description = "Colorful worry-free console applications for Linux, Mac OS X, and Windows." +optional = false +python-versions = ">=2.6" +files = [ + {file = "colorclass-2.2.2-py2.py3-none-any.whl", hash = "sha256:6f10c273a0ef7a1150b1120b6095cbdd68e5cf36dfd5d0fc957a2500bbf99a55"}, + {file = "colorclass-2.2.2.tar.gz", hash = "sha256:6d4fe287766166a98ca7bc6f6312daf04a0481b1eda43e7173484051c0ab4366"}, +] + +[[package]] +name = "coverage" +version = "7.6.0" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dff044f661f59dace805eedb4a7404c573b6ff0cdba4a524141bc63d7be5c7fd"}, + {file = "coverage-7.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a8659fd33ee9e6ca03950cfdcdf271d645cf681609153f218826dd9805ab585c"}, + {file = "coverage-7.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7792f0ab20df8071d669d929c75c97fecfa6bcab82c10ee4adb91c7a54055463"}, + {file = "coverage-7.6.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4b3cd1ca7cd73d229487fa5caca9e4bc1f0bca96526b922d61053ea751fe791"}, + {file = "coverage-7.6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7e128f85c0b419907d1f38e616c4f1e9f1d1b37a7949f44df9a73d5da5cd53c"}, + {file = "coverage-7.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a94925102c89247530ae1dab7dc02c690942566f22e189cbd53579b0693c0783"}, + {file = "coverage-7.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dcd070b5b585b50e6617e8972f3fbbee786afca71b1936ac06257f7e178f00f6"}, + {file = "coverage-7.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d50a252b23b9b4dfeefc1f663c568a221092cbaded20a05a11665d0dbec9b8fb"}, + {file = "coverage-7.6.0-cp310-cp310-win32.whl", hash = "sha256:0e7b27d04131c46e6894f23a4ae186a6a2207209a05df5b6ad4caee6d54a222c"}, + {file = "coverage-7.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:54dece71673b3187c86226c3ca793c5f891f9fc3d8aa183f2e3653da18566169"}, + {file = "coverage-7.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7b525ab52ce18c57ae232ba6f7010297a87ced82a2383b1afd238849c1ff933"}, + {file = "coverage-7.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bea27c4269234e06f621f3fac3925f56ff34bc14521484b8f66a580aacc2e7d"}, + {file = "coverage-7.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed8d1d1821ba5fc88d4a4f45387b65de52382fa3ef1f0115a4f7a20cdfab0e94"}, + {file = "coverage-7.6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01c322ef2bbe15057bc4bf132b525b7e3f7206f071799eb8aa6ad1940bcf5fb1"}, + {file = "coverage-7.6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03cafe82c1b32b770a29fd6de923625ccac3185a54a5e66606da26d105f37dac"}, + {file = "coverage-7.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0d1b923fc4a40c5832be4f35a5dab0e5ff89cddf83bb4174499e02ea089daf57"}, + {file = "coverage-7.6.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4b03741e70fb811d1a9a1d75355cf391f274ed85847f4b78e35459899f57af4d"}, + {file = "coverage-7.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a73d18625f6a8a1cbb11eadc1d03929f9510f4131879288e3f7922097a429f63"}, + {file = "coverage-7.6.0-cp311-cp311-win32.whl", hash = "sha256:65fa405b837060db569a61ec368b74688f429b32fa47a8929a7a2f9b47183713"}, + {file = "coverage-7.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:6379688fb4cfa921ae349c76eb1a9ab26b65f32b03d46bb0eed841fd4cb6afb1"}, + {file = "coverage-7.6.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f7db0b6ae1f96ae41afe626095149ecd1b212b424626175a6633c2999eaad45b"}, + {file = "coverage-7.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bbdf9a72403110a3bdae77948b8011f644571311c2fb35ee15f0f10a8fc082e8"}, + {file = "coverage-7.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc44bf0315268e253bf563f3560e6c004efe38f76db03a1558274a6e04bf5d5"}, + {file = "coverage-7.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da8549d17489cd52f85a9829d0e1d91059359b3c54a26f28bec2c5d369524807"}, + {file = "coverage-7.6.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0086cd4fc71b7d485ac93ca4239c8f75732c2ae3ba83f6be1c9be59d9e2c6382"}, + {file = "coverage-7.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1fad32ee9b27350687035cb5fdf9145bc9cf0a094a9577d43e909948ebcfa27b"}, + {file = "coverage-7.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:044a0985a4f25b335882b0966625270a8d9db3d3409ddc49a4eb00b0ef5e8cee"}, + {file = "coverage-7.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:76d5f82213aa78098b9b964ea89de4617e70e0d43e97900c2778a50856dac605"}, + {file = "coverage-7.6.0-cp312-cp312-win32.whl", hash = "sha256:3c59105f8d58ce500f348c5b56163a4113a440dad6daa2294b5052a10db866da"}, + {file = "coverage-7.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:ca5d79cfdae420a1d52bf177de4bc2289c321d6c961ae321503b2ca59c17ae67"}, + {file = "coverage-7.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d39bd10f0ae453554798b125d2f39884290c480f56e8a02ba7a6ed552005243b"}, + {file = "coverage-7.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:beb08e8508e53a568811016e59f3234d29c2583f6b6e28572f0954a6b4f7e03d"}, + {file = "coverage-7.6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2e16f4cd2bc4d88ba30ca2d3bbf2f21f00f382cf4e1ce3b1ddc96c634bc48ca"}, + {file = "coverage-7.6.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6616d1c9bf1e3faea78711ee42a8b972367d82ceae233ec0ac61cc7fec09fa6b"}, + {file = "coverage-7.6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad4567d6c334c46046d1c4c20024de2a1c3abc626817ae21ae3da600f5779b44"}, + {file = "coverage-7.6.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d17c6a415d68cfe1091d3296ba5749d3d8696e42c37fca5d4860c5bf7b729f03"}, + {file = "coverage-7.6.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9146579352d7b5f6412735d0f203bbd8d00113a680b66565e205bc605ef81bc6"}, + {file = "coverage-7.6.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cdab02a0a941af190df8782aafc591ef3ad08824f97850b015c8c6a8b3877b0b"}, + {file = "coverage-7.6.0-cp38-cp38-win32.whl", hash = "sha256:df423f351b162a702c053d5dddc0fc0ef9a9e27ea3f449781ace5f906b664428"}, + {file = "coverage-7.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:f2501d60d7497fd55e391f423f965bbe9e650e9ffc3c627d5f0ac516026000b8"}, + {file = "coverage-7.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7221f9ac9dad9492cecab6f676b3eaf9185141539d5c9689d13fd6b0d7de840c"}, + {file = "coverage-7.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ddaaa91bfc4477d2871442bbf30a125e8fe6b05da8a0015507bfbf4718228ab2"}, + {file = "coverage-7.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4cbe651f3904e28f3a55d6f371203049034b4ddbce65a54527a3f189ca3b390"}, + {file = "coverage-7.6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:831b476d79408ab6ccfadaaf199906c833f02fdb32c9ab907b1d4aa0713cfa3b"}, + {file = "coverage-7.6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46c3d091059ad0b9c59d1034de74a7f36dcfa7f6d3bde782c49deb42438f2450"}, + {file = "coverage-7.6.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4d5fae0a22dc86259dee66f2cc6c1d3e490c4a1214d7daa2a93d07491c5c04b6"}, + {file = "coverage-7.6.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:07ed352205574aad067482e53dd606926afebcb5590653121063fbf4e2175166"}, + {file = "coverage-7.6.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:49c76cdfa13015c4560702574bad67f0e15ca5a2872c6a125f6327ead2b731dd"}, + {file = "coverage-7.6.0-cp39-cp39-win32.whl", hash = "sha256:482855914928c8175735a2a59c8dc5806cf7d8f032e4820d52e845d1f731dca2"}, + {file = "coverage-7.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:543ef9179bc55edfd895154a51792b01c017c87af0ebaae092720152e19e42ca"}, + {file = "coverage-7.6.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:6fe885135c8a479d3e37a7aae61cbd3a0fb2deccb4dda3c25f92a49189f766d6"}, + {file = "coverage-7.6.0.tar.gz", hash = "sha256:289cc803fa1dc901f84701ac10c9ee873619320f2f9aff38794db4a4a0268d51"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "distlib" +version = "0.3.8" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "execnet" +version = "2.1.1" +description = "execnet: rapid multi-Python deployment" +optional = false +python-versions = ">=3.8" +files = [ + {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, + {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, +] + +[package.extras] +testing = ["hatch", "pre-commit", "pytest", "tox"] + +[[package]] +name = "filelock" +version = "3.15.4" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, + {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] +typing = ["typing-extensions (>=4.8)"] + +[[package]] +name = "identify" +version = "2.6.0" +description = "File identification library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, + {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "importlib-metadata" +version = "8.0.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-8.0.0-py3-none-any.whl", hash = "sha256:15584cf2b1bf449d98ff8a6ff1abef57bf20f3ac6454f431736cd3e660921b2f"}, + {file = "importlib_metadata-8.0.0.tar.gz", hash = "sha256:188bd24e4c346d3f0a933f275c2fec67050326a856b9a359881d7c2a697e8812"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "mypy" +version = "1.10.1" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e36f229acfe250dc660790840916eb49726c928e8ce10fbdf90715090fe4ae02"}, + {file = "mypy-1.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:51a46974340baaa4145363b9e051812a2446cf583dfaeba124af966fa44593f7"}, + {file = "mypy-1.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:901c89c2d67bba57aaaca91ccdb659aa3a312de67f23b9dfb059727cce2e2e0a"}, + {file = "mypy-1.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0cd62192a4a32b77ceb31272d9e74d23cd88c8060c34d1d3622db3267679a5d9"}, + {file = "mypy-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:a2cbc68cb9e943ac0814c13e2452d2046c2f2b23ff0278e26599224cf164e78d"}, + {file = "mypy-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bd6f629b67bb43dc0d9211ee98b96d8dabc97b1ad38b9b25f5e4c4d7569a0c6a"}, + {file = "mypy-1.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1bbb3a6f5ff319d2b9d40b4080d46cd639abe3516d5a62c070cf0114a457d84"}, + {file = "mypy-1.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8edd4e9bbbc9d7b79502eb9592cab808585516ae1bcc1446eb9122656c6066f"}, + {file = "mypy-1.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6166a88b15f1759f94a46fa474c7b1b05d134b1b61fca627dd7335454cc9aa6b"}, + {file = "mypy-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bb9cd11c01c8606a9d0b83ffa91d0b236a0e91bc4126d9ba9ce62906ada868e"}, + {file = "mypy-1.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d8681909f7b44d0b7b86e653ca152d6dff0eb5eb41694e163c6092124f8246d7"}, + {file = "mypy-1.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:378c03f53f10bbdd55ca94e46ec3ba255279706a6aacaecac52ad248f98205d3"}, + {file = "mypy-1.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bacf8f3a3d7d849f40ca6caea5c055122efe70e81480c8328ad29c55c69e93e"}, + {file = "mypy-1.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:701b5f71413f1e9855566a34d6e9d12624e9e0a8818a5704d74d6b0402e66c04"}, + {file = "mypy-1.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c4c2992f6ea46ff7fce0072642cfb62af7a2484efe69017ed8b095f7b39ef31"}, + {file = "mypy-1.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:604282c886497645ffb87b8f35a57ec773a4a2721161e709a4422c1636ddde5c"}, + {file = "mypy-1.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37fd87cab83f09842653f08de066ee68f1182b9b5282e4634cdb4b407266bade"}, + {file = "mypy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8addf6313777dbb92e9564c5d32ec122bf2c6c39d683ea64de6a1fd98b90fe37"}, + {file = "mypy-1.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cc3ca0a244eb9a5249c7c583ad9a7e881aa5d7b73c35652296ddcdb33b2b9c7"}, + {file = "mypy-1.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:1b3a2ffce52cc4dbaeee4df762f20a2905aa171ef157b82192f2e2f368eec05d"}, + {file = "mypy-1.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe85ed6836165d52ae8b88f99527d3d1b2362e0cb90b005409b8bed90e9059b3"}, + {file = "mypy-1.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2ae450d60d7d020d67ab440c6e3fae375809988119817214440033f26ddf7bf"}, + {file = "mypy-1.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be84c06e6abd72f960ba9a71561c14137a583093ffcf9bbfaf5e613d63fa531"}, + {file = "mypy-1.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2189ff1e39db399f08205e22a797383613ce1cb0cb3b13d8bcf0170e45b96cc3"}, + {file = "mypy-1.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:97a131ee36ac37ce9581f4220311247ab6cba896b4395b9c87af0675a13a755f"}, + {file = "mypy-1.10.1-py3-none-any.whl", hash = "sha256:71d8ac0b906354ebda8ef1673e5fde785936ac1f29ff6987c7483cfbd5a4235a"}, + {file = "mypy-1.10.1.tar.gz", hash = "sha256:1f8f492d7db9e3593ef42d4f115f04e556130f2819ad33ab84551403e97dd4c0"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.1.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +description = "Node.js virtual environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, +] + +[[package]] +name = "packaging" +version = "24.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "pbr" +version = "6.0.0" +description = "Python Build Reasonableness" +optional = false +python-versions = ">=2.6" +files = [ + {file = "pbr-6.0.0-py2.py3-none-any.whl", hash = "sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda"}, + {file = "pbr-6.0.0.tar.gz", hash = "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9"}, +] + +[[package]] +name = "pillow" +version = "10.4.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, + {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"}, + {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"}, + {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"}, + {file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"}, + {file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"}, + {file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"}, + {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, + {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"}, + {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"}, + {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"}, + {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"}, + {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"}, + {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"}, + {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"}, + {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"}, + {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"}, + {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"}, + {file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"}, + {file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"}, + {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"}, + {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"}, + {file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"}, + {file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"}, + {file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"}, + {file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"}, + {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"}, + {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"}, + {file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"}, + {file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"}, + {file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"}, + {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] + +[[package]] +name = "platformdirs" +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "3.5.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"}, + {file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "pygments" +version = "2.18.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pylint" +version = "3.2.5" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.2.5-py3-none-any.whl", hash = "sha256:32cd6c042b5004b8e857d727708720c54a676d1e22917cf1a2df9b4d4868abd6"}, + {file = "pylint-3.2.5.tar.gz", hash = "sha256:e9b7171e242dcc6ebd0aaa7540481d1a72860748a0a7816b8fe6cf6c80a6fe7e"}, +] + +[package.dependencies] +astroid = ">=3.2.2,<=3.3.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = [ + {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, +] +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +tomlkit = ">=0.10.1" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pyproject-api" +version = "1.7.1" +description = "API to interact with the python pyproject.toml based projects" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyproject_api-1.7.1-py3-none-any.whl", hash = "sha256:2dc1654062c2b27733d8fd4cdda672b22fe8741ef1dde8e3a998a9547b071eeb"}, + {file = "pyproject_api-1.7.1.tar.gz", hash = "sha256:7ebc6cd10710f89f4cf2a2731710a98abce37ebff19427116ff2174c9236a827"}, +] + +[package.dependencies] +packaging = ">=24.1" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +docs = ["furo (>=2024.5.6)", "sphinx-autodoc-typehints (>=2.2.1)"] +testing = ["covdefaults (>=2.3)", "pytest (>=8.2.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "setuptools (>=70.1)"] + +[[package]] +name = "pytest" +version = "8.2.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, + {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "5.0.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] + +[[package]] +name = "pytest-network" +version = "0.0.1" +description = "A simple plugin to disable network on socket level." +optional = false +python-versions = "*" +files = [ + {file = "pytest_network-0.0.1.tar.gz", hash = "sha256:7b109e2ad27f4ab0f13c1796a9b419bab1d503ea3b5ae3ca37363486ccceaa3f"}, +] + +[package.dependencies] +pytest = ">=5.3.1" + +[[package]] +name = "pytest-randomly" +version = "3.15.0" +description = "Pytest plugin to randomly order tests and control random.seed." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_randomly-3.15.0-py3-none-any.whl", hash = "sha256:0516f4344b29f4e9cdae8bce31c4aeebf59d0b9ef05927c33354ff3859eeeca6"}, + {file = "pytest_randomly-3.15.0.tar.gz", hash = "sha256:b908529648667ba5e54723088edd6f82252f540cc340d748d1fa985539687047"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} +pytest = "*" + +[[package]] +name = "pytest-sugar" +version = "1.0.0" +description = "pytest-sugar is a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly)." +optional = false +python-versions = "*" +files = [ + {file = "pytest-sugar-1.0.0.tar.gz", hash = "sha256:6422e83258f5b0c04ce7c632176c7732cab5fdb909cb39cca5c9139f81276c0a"}, + {file = "pytest_sugar-1.0.0-py3-none-any.whl", hash = "sha256:70ebcd8fc5795dc457ff8b69d266a4e2e8a74ae0c3edc749381c64b5246c8dfd"}, +] + +[package.dependencies] +packaging = ">=21.3" +pytest = ">=6.2.0" +termcolor = ">=2.1.0" + +[package.extras] +dev = ["black", "flake8", "pre-commit"] + +[[package]] +name = "pytest-xdist" +version = "3.6.1" +description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, + {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, +] + +[package.dependencies] +execnet = ">=2.1" +pytest = ">=7.0.0" + +[package.extras] +psutil = ["psutil (>=3.0)"] +setproctitle = ["setproctitle"] +testing = ["filelock"] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "rich" +version = "13.7.1" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "ruff" +version = "0.5.2" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.5.2-py3-none-linux_armv6l.whl", hash = "sha256:7bab8345df60f9368d5f4594bfb8b71157496b44c30ff035d1d01972e764d3be"}, + {file = "ruff-0.5.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:1aa7acad382ada0189dbe76095cf0a36cd0036779607c397ffdea16517f535b1"}, + {file = "ruff-0.5.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:aec618d5a0cdba5592c60c2dee7d9c865180627f1a4a691257dea14ac1aa264d"}, + {file = "ruff-0.5.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b62adc5ce81780ff04077e88bac0986363e4a3260ad3ef11ae9c14aa0e67ef"}, + {file = "ruff-0.5.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dc42ebf56ede83cb080a50eba35a06e636775649a1ffd03dc986533f878702a3"}, + {file = "ruff-0.5.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c15c6e9f88c67ffa442681365d11df38afb11059fc44238e71a9d9f1fd51de70"}, + {file = "ruff-0.5.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d3de9a5960f72c335ef00763d861fc5005ef0644cb260ba1b5a115a102157251"}, + {file = "ruff-0.5.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe5a968ae933e8f7627a7b2fc8893336ac2be0eb0aace762d3421f6e8f7b7f83"}, + {file = "ruff-0.5.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a04f54a9018f75615ae52f36ea1c5515e356e5d5e214b22609ddb546baef7132"}, + {file = "ruff-0.5.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed02fb52e3741f0738db5f93e10ae0fb5c71eb33a4f2ba87c9a2fa97462a649"}, + {file = "ruff-0.5.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3cf8fe659f6362530435d97d738eb413e9f090e7e993f88711b0377fbdc99f60"}, + {file = "ruff-0.5.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:237a37e673e9f3cbfff0d2243e797c4862a44c93d2f52a52021c1a1b0899f846"}, + {file = "ruff-0.5.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2a2949ce7c1cbd8317432ada80fe32156df825b2fd611688814c8557824ef060"}, + {file = "ruff-0.5.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:481af57c8e99da92ad168924fd82220266043c8255942a1cb87958b108ac9335"}, + {file = "ruff-0.5.2-py3-none-win32.whl", hash = "sha256:f1aea290c56d913e363066d83d3fc26848814a1fed3d72144ff9c930e8c7c718"}, + {file = "ruff-0.5.2-py3-none-win_amd64.whl", hash = "sha256:8532660b72b5d94d2a0a7a27ae7b9b40053662d00357bb2a6864dd7e38819084"}, + {file = "ruff-0.5.2-py3-none-win_arm64.whl", hash = "sha256:73439805c5cb68f364d826a5c5c4b6c798ded6b7ebaa4011f01ce6c94e4d5583"}, + {file = "ruff-0.5.2.tar.gz", hash = "sha256:2c0df2d2de685433794a14d8d2e240df619b748fbe3367346baa519d8e6f1ca2"}, +] + +[[package]] +name = "stevedore" +version = "5.2.0" +description = "Manage dynamic plugins for Python applications" +optional = false +python-versions = ">=3.8" +files = [ + {file = "stevedore-5.2.0-py3-none-any.whl", hash = "sha256:1c15d95766ca0569cad14cb6272d4d31dae66b011a929d7c18219c176ea1b5c9"}, + {file = "stevedore-5.2.0.tar.gz", hash = "sha256:46b93ca40e1114cea93d738a6c1e365396981bb6bb78c27045b7587c9473544d"}, +] + +[package.dependencies] +pbr = ">=2.0.0,<2.1.0 || >2.1.0" + +[[package]] +name = "termcolor" +version = "2.4.0" +description = "ANSI color formatting for output in terminal" +optional = false +python-versions = ">=3.8" +files = [ + {file = "termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63"}, + {file = "termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tomlkit" +version = "0.13.0" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomlkit-0.13.0-py3-none-any.whl", hash = "sha256:7075d3042d03b80f603482d69bf0c8f345c2b30e41699fd8883227f89972b264"}, + {file = "tomlkit-0.13.0.tar.gz", hash = "sha256:08ad192699734149f5b97b45f1f18dad7eb1b6d16bc72ad0c2335772650d7b72"}, +] + +[[package]] +name = "tox" +version = "4.16.0" +description = "tox is a generic virtualenv management and test command line tool" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tox-4.16.0-py3-none-any.whl", hash = "sha256:61e101061b977b46cf00093d4319438055290ad0009f84497a07bf2d2d7a06d0"}, + {file = "tox-4.16.0.tar.gz", hash = "sha256:43499656f9949edb681c0f907f86fbfee98677af9919d8b11ae5ad77cb800748"}, +] + +[package.dependencies] +cachetools = ">=5.3.3" +chardet = ">=5.2" +colorama = ">=0.4.6" +filelock = ">=3.15.4" +packaging = ">=24.1" +platformdirs = ">=4.2.2" +pluggy = ">=1.5" +pyproject-api = ">=1.7.1" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} +virtualenv = ">=20.26.3" + +[package.extras] +docs = ["furo (>=2024.5.6)", "sphinx (>=7.3.7)", "sphinx-argparse-cli (>=1.16)", "sphinx-autodoc-typehints (>=2.2.2)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.11)"] +testing = ["build[virtualenv] (>=1.2.1)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1)", "diff-cover (>=9.1)", "distlib (>=0.3.8)", "flaky (>=3.8.1)", "hatch-vcs (>=0.4)", "hatchling (>=1.25)", "psutil (>=6)", "pytest (>=8.2.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-xdist (>=3.6.1)", "re-assert (>=1.1)", "setuptools (>=70.2)", "time-machine (>=2.14.2)", "wheel (>=0.43)"] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "vermin" +version = "1.6.0" +description = "Concurrently detect the minimum Python versions needed to run code" +optional = false +python-versions = ">=3.0" +files = [ + {file = "vermin-1.6.0-py2.py3-none-any.whl", hash = "sha256:f1fa9ee40f59983dc40e0477eb2b1fa8061a3df4c3b2bcf349add462a5610efb"}, + {file = "vermin-1.6.0.tar.gz", hash = "sha256:6266ca02f55d1c2aa189a610017c132eb2d1934f09e72a955b1eb3820ee6d4ef"}, +] + +[[package]] +name = "virtualenv" +version = "20.26.3" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, + {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + +[[package]] +name = "zipp" +version = "3.19.2" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, + {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, +] + +[package.extras] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.8" +content-hash = "606c91a442e97c389b0e113520eef888b302b71d9d24ae48f90915cc30ec7190" diff --git a/pyproject-whl.toml b/pyproject-whl.toml deleted file mode 100644 index 3c186b7..0000000 --- a/pyproject-whl.toml +++ /dev/null @@ -1,66 +0,0 @@ -[tool.poetry] -name = "terminaltables-whl" -version = "3.1.0" -description = "Generate simple tables in terminals from a nested list of strings." -authors = [ - "Matthew Martin <matthewdeanmartin@gmail.com>", - "Robpol86 <robpol86@gmail.com>", -] -keywords = ["Shell","Bash","ANSI","ASCII","terminal","tables"] -classifiers = [ - "Development Status :: 5 - Production/Stable", - "Environment :: Console", - "Environment :: MacOS X", - "Environment :: Win32 (MS Windows)", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Operating System :: MacOS :: MacOS X", - "Operating System :: Microsoft :: Windows", - "Operating System :: POSIX :: Linux", - "Operating System :: POSIX", - "Programming Language :: Python :: 2.6", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3.3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: Implementation :: PyPy", - "Topic :: Software Development :: Libraries", - "Topic :: Terminals", - "Topic :: Text Processing :: Markup", -] -packages = [ - { include = "terminaltables" }, -] -include = [ - "terminaltables/**/*.py", -] -exclude = [ -] -license = "MIT" -readme = "README.md" -repository = "https://github.com/matthewdeanmartin/terminaltables" -homepage = "https://github.com/matthewdeanmartin/terminaltables" -documentation = "https://github.com/matthewdeanmartin/terminaltables" - -[tool.poetry.urls] -"Bug Tracker" = "https://github.com/matthewdeanmartin/terminaltables/issues" -"Change Log" = "https://github.com/matthewdeanmartin/terminaltables/blob/main/docs/CHANGES.MD" - -[tool.poetry.scripts] - - -[tool.poetry.dependencies] -# per vermin's estimation -python = ">=2.6 || >=3.0" - -[tool.poetry.dev-dependencies] -pytest = "==6.0.1" - -[build-system] -requires = ["poetry>=0.12"] -build-backend = "poetry.masonry.api" diff --git a/pyproject.toml b/pyproject.toml index bdcd0ce..49dfb74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry]
-name = "terminaltables"
-version = "3.1.10"
-description = "Generate simple tables in terminals from a nested list of strings."
+name = "terminaltables3"
+version = "4.0.0"
+description = "Generate simple tables in terminals from a nested list of strings. Fork of terminaltables."
authors = [
"Robpol86 <robpol86@gmail.com>",
"Matthew Martin <matthewdeanmartin@gmail.com>"
@@ -18,49 +18,74 @@ classifiers = [ "Operating System :: Microsoft :: Windows",
"Operating System :: POSIX :: Linux",
"Operating System :: POSIX",
- "Programming Language :: Python :: 2.6",
- "Programming Language :: Python :: 2.7",
- "Programming Language :: Python :: 3.3",
- "Programming Language :: Python :: 3.4",
- "Programming Language :: Python :: 3.5",
- "Programming Language :: Python :: 3.6",
- "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Software Development :: Libraries",
"Topic :: Terminals",
"Topic :: Text Processing :: Markup",
]
packages = [
- { include = "terminaltables" },
+ { include = "terminaltables3" },
]
include = [
- "terminaltables/**/*.py",
+ "terminaltables3/**/*.py",
]
exclude = [
]
license = "MIT"
readme = "README.md"
-repository = "https://github.com/matthewdeanmartin/terminaltables"
-homepage = "https://github.com/matthewdeanmartin/terminaltables"
-documentation = "https://github.com/matthewdeanmartin/terminaltables"
+repository = "https://github.com/matthewdeanmartin/terminaltables3"
+homepage = "https://github.com/matthewdeanmartin/terminaltables3"
+documentation = "https://github.com/matthewdeanmartin/terminaltables3"
[tool.poetry.urls]
-"Bug Tracker" = "https://github.com/matthewdeanmartin/terminaltables/issues"
-"Change Log" = "https://github.com/matthewdeanmartin/terminaltables/blob/master/CHANGELOG.md"
+"Bug Tracker" = "https://github.com/matthewdeanmartin/terminaltables3/issues"
+"Change Log" = "https://github.com/matthewdeanmartin/terminaltables3/blob/master/CHANGELOG.md"
[tool.poetry.scripts]
[tool.poetry.dependencies]
# per vermin's estimation
-python = ">=2.6 || >=3.0"
+python = ">=3.8"
[tool.poetry.dev-dependencies]
-pytest = "==6.0.1"
+# complementary deps
+colorama=">=0.3.7"
+colorclass=">=2.2.0"
+termcolor = "*"
+
+# build deps
+pytest = ">=8.2.1"
+pytest-cov = ">=5.0.0"
+vermin = ">=1.6.0"
+tox = ">=4.15.0"
+isort = ">=5.13.2"
+bandit = ">=1.7.8"
+black = ">=24.4.2"
+pylint = ">=3.2.2"
+ruff = ">=0.4.5"
+mypy = ">=1.10.0"
+pillow = ">=10.3.0"
+pre-commit = ">=2"
+
+# pytest = ">=7.4.3"
+pytest-xdist =">=3.5.0"
+pytest-randomly=">=3.15.0"
+pytest-sugar =">=0.9.7"
+
+# pytest-snapshot = "*"
+# pytest-asyncio = "*"
+pytest-network= "*"
+
+
+# check-wheel-contents = "*" # requires <4
[build-system]
-requires = ["poetry>=0.12"]
-build-backend = "poetry.masonry.api"
+requires = ["poetry-core>=1.0.0"]
+build-backend = "poetry.core.masonry.api"
diff --git a/terminaltables/__init__.py b/terminaltables/__init__.py deleted file mode 100644 index 6cea813..0000000 --- a/terminaltables/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Generate simple tables in terminals from a nested list of strings. - -Use SingleTable or DoubleTable instead of AsciiTable for box-drawing characters. - -https://github.com/Robpol86/terminaltables -https://pypi.python.org/pypi/terminaltables -""" - -from terminaltables.ascii_table import AsciiTable # noqa -from terminaltables.github_table import GithubFlavoredMarkdownTable # noqa -from terminaltables.other_tables import DoubleTable # noqa -from terminaltables.other_tables import SingleTable # noqa -from terminaltables.other_tables import PorcelainTable # noqa - -__author__ = '@Robpol86' -__license__ = 'MIT' -__version__ = '3.1.0' diff --git a/terminaltables/other_tables.py b/terminaltables/other_tables.py deleted file mode 100644 index 50c0bcd..0000000 --- a/terminaltables/other_tables.py +++ /dev/null @@ -1,177 +0,0 @@ -"""Additional simple tables defined here.""" - -from terminaltables.ascii_table import AsciiTable -from terminaltables.terminal_io import IS_WINDOWS - - -class UnixTable(AsciiTable): - """Draw a table using box-drawing characters on Unix platforms. Table borders won't have any gaps between lines. - - Similar to the tables shown on PC BIOS boot messages, but not double-lined. - """ - - CHAR_F_INNER_HORIZONTAL = '\033(0\x71\033(B' - CHAR_F_INNER_INTERSECT = '\033(0\x6e\033(B' - CHAR_F_INNER_VERTICAL = '\033(0\x78\033(B' - CHAR_F_OUTER_LEFT_INTERSECT = '\033(0\x74\033(B' - CHAR_F_OUTER_LEFT_VERTICAL = '\033(0\x78\033(B' - CHAR_F_OUTER_RIGHT_INTERSECT = '\033(0\x75\033(B' - CHAR_F_OUTER_RIGHT_VERTICAL = '\033(0\x78\033(B' - CHAR_H_INNER_HORIZONTAL = '\033(0\x71\033(B' - CHAR_H_INNER_INTERSECT = '\033(0\x6e\033(B' - CHAR_H_INNER_VERTICAL = '\033(0\x78\033(B' - CHAR_H_OUTER_LEFT_INTERSECT = '\033(0\x74\033(B' - CHAR_H_OUTER_LEFT_VERTICAL = '\033(0\x78\033(B' - CHAR_H_OUTER_RIGHT_INTERSECT = '\033(0\x75\033(B' - CHAR_H_OUTER_RIGHT_VERTICAL = '\033(0\x78\033(B' - CHAR_INNER_HORIZONTAL = '\033(0\x71\033(B' - CHAR_INNER_INTERSECT = '\033(0\x6e\033(B' - CHAR_INNER_VERTICAL = '\033(0\x78\033(B' - CHAR_OUTER_BOTTOM_HORIZONTAL = '\033(0\x71\033(B' - CHAR_OUTER_BOTTOM_INTERSECT = '\033(0\x76\033(B' - CHAR_OUTER_BOTTOM_LEFT = '\033(0\x6d\033(B' - CHAR_OUTER_BOTTOM_RIGHT = '\033(0\x6a\033(B' - CHAR_OUTER_LEFT_INTERSECT = '\033(0\x74\033(B' - CHAR_OUTER_LEFT_VERTICAL = '\033(0\x78\033(B' - CHAR_OUTER_RIGHT_INTERSECT = '\033(0\x75\033(B' - CHAR_OUTER_RIGHT_VERTICAL = '\033(0\x78\033(B' - CHAR_OUTER_TOP_HORIZONTAL = '\033(0\x71\033(B' - CHAR_OUTER_TOP_INTERSECT = '\033(0\x77\033(B' - CHAR_OUTER_TOP_LEFT = '\033(0\x6c\033(B' - CHAR_OUTER_TOP_RIGHT = '\033(0\x6b\033(B' - - @property - def table(self): - """Return a large string of the entire table ready to be printed to the terminal.""" - ascii_table = super(UnixTable, self).table - optimized = ascii_table.replace('\033(B\033(0', '') - return optimized - - -class WindowsTable(AsciiTable): - """Draw a table using box-drawing characters on Windows platforms. This uses Code Page 437. Single-line borders. - - From: http://en.wikipedia.org/wiki/Code_page_437#Characters - """ - - CHAR_F_INNER_HORIZONTAL = b'\xc4'.decode('ibm437') - CHAR_F_INNER_INTERSECT = b'\xc5'.decode('ibm437') - CHAR_F_INNER_VERTICAL = b'\xb3'.decode('ibm437') - CHAR_F_OUTER_LEFT_INTERSECT = b'\xc3'.decode('ibm437') - CHAR_F_OUTER_LEFT_VERTICAL = b'\xb3'.decode('ibm437') - CHAR_F_OUTER_RIGHT_INTERSECT = b'\xb4'.decode('ibm437') - CHAR_F_OUTER_RIGHT_VERTICAL = b'\xb3'.decode('ibm437') - CHAR_H_INNER_HORIZONTAL = b'\xc4'.decode('ibm437') - CHAR_H_INNER_INTERSECT = b'\xc5'.decode('ibm437') - CHAR_H_INNER_VERTICAL = b'\xb3'.decode('ibm437') - CHAR_H_OUTER_LEFT_INTERSECT = b'\xc3'.decode('ibm437') - CHAR_H_OUTER_LEFT_VERTICAL = b'\xb3'.decode('ibm437') - CHAR_H_OUTER_RIGHT_INTERSECT = b'\xb4'.decode('ibm437') - CHAR_H_OUTER_RIGHT_VERTICAL = b'\xb3'.decode('ibm437') - CHAR_INNER_HORIZONTAL = b'\xc4'.decode('ibm437') - CHAR_INNER_INTERSECT = b'\xc5'.decode('ibm437') - CHAR_INNER_VERTICAL = b'\xb3'.decode('ibm437') - CHAR_OUTER_BOTTOM_HORIZONTAL = b'\xc4'.decode('ibm437') - CHAR_OUTER_BOTTOM_INTERSECT = b'\xc1'.decode('ibm437') - CHAR_OUTER_BOTTOM_LEFT = b'\xc0'.decode('ibm437') - CHAR_OUTER_BOTTOM_RIGHT = b'\xd9'.decode('ibm437') - CHAR_OUTER_LEFT_INTERSECT = b'\xc3'.decode('ibm437') - CHAR_OUTER_LEFT_VERTICAL = b'\xb3'.decode('ibm437') - CHAR_OUTER_RIGHT_INTERSECT = b'\xb4'.decode('ibm437') - CHAR_OUTER_RIGHT_VERTICAL = b'\xb3'.decode('ibm437') - CHAR_OUTER_TOP_HORIZONTAL = b'\xc4'.decode('ibm437') - CHAR_OUTER_TOP_INTERSECT = b'\xc2'.decode('ibm437') - CHAR_OUTER_TOP_LEFT = b'\xda'.decode('ibm437') - CHAR_OUTER_TOP_RIGHT = b'\xbf'.decode('ibm437') - - -class WindowsTableDouble(AsciiTable): - """Draw a table using box-drawing characters on Windows platforms. This uses Code Page 437. Double-line borders.""" - - CHAR_F_INNER_HORIZONTAL = b'\xcd'.decode('ibm437') - CHAR_F_INNER_INTERSECT = b'\xce'.decode('ibm437') - CHAR_F_INNER_VERTICAL = b'\xba'.decode('ibm437') - CHAR_F_OUTER_LEFT_INTERSECT = b'\xcc'.decode('ibm437') - CHAR_F_OUTER_LEFT_VERTICAL = b'\xba'.decode('ibm437') - CHAR_F_OUTER_RIGHT_INTERSECT = b'\xb9'.decode('ibm437') - CHAR_F_OUTER_RIGHT_VERTICAL = b'\xba'.decode('ibm437') - CHAR_H_INNER_HORIZONTAL = b'\xcd'.decode('ibm437') - CHAR_H_INNER_INTERSECT = b'\xce'.decode('ibm437') - CHAR_H_INNER_VERTICAL = b'\xba'.decode('ibm437') - CHAR_H_OUTER_LEFT_INTERSECT = b'\xcc'.decode('ibm437') - CHAR_H_OUTER_LEFT_VERTICAL = b'\xba'.decode('ibm437') - CHAR_H_OUTER_RIGHT_INTERSECT = b'\xb9'.decode('ibm437') - CHAR_H_OUTER_RIGHT_VERTICAL = b'\xba'.decode('ibm437') - CHAR_INNER_HORIZONTAL = b'\xcd'.decode('ibm437') - CHAR_INNER_INTERSECT = b'\xce'.decode('ibm437') - CHAR_INNER_VERTICAL = b'\xba'.decode('ibm437') - CHAR_OUTER_BOTTOM_HORIZONTAL = b'\xcd'.decode('ibm437') - CHAR_OUTER_BOTTOM_INTERSECT = b'\xca'.decode('ibm437') - CHAR_OUTER_BOTTOM_LEFT = b'\xc8'.decode('ibm437') - CHAR_OUTER_BOTTOM_RIGHT = b'\xbc'.decode('ibm437') - CHAR_OUTER_LEFT_INTERSECT = b'\xcc'.decode('ibm437') - CHAR_OUTER_LEFT_VERTICAL = b'\xba'.decode('ibm437') - CHAR_OUTER_RIGHT_INTERSECT = b'\xb9'.decode('ibm437') - CHAR_OUTER_RIGHT_VERTICAL = b'\xba'.decode('ibm437') - CHAR_OUTER_TOP_HORIZONTAL = b'\xcd'.decode('ibm437') - CHAR_OUTER_TOP_INTERSECT = b'\xcb'.decode('ibm437') - CHAR_OUTER_TOP_LEFT = b'\xc9'.decode('ibm437') - CHAR_OUTER_TOP_RIGHT = b'\xbb'.decode('ibm437') - - -class SingleTable(WindowsTable if IS_WINDOWS else UnixTable): - """Cross-platform table with single-line box-drawing characters. - - :ivar iter table_data: List (empty or list of lists of strings) representing the table. - :ivar str title: Optional title to show within the top border of the table. - :ivar bool inner_column_border: Separates columns. - :ivar bool inner_footing_row_border: Show a border before the last row. - :ivar bool inner_heading_row_border: Show a border after the first row. - :ivar bool inner_row_border: Show a border in between every row. - :ivar bool outer_border: Show the top, left, right, and bottom border. - :ivar dict justify_columns: Horizontal justification. Keys are column indexes (int). Values are right/left/center. - :ivar int padding_left: Number of spaces to pad on the left side of every cell. - :ivar int padding_right: Number of spaces to pad on the right side of every cell. - """ - - pass - - -class DoubleTable(WindowsTableDouble): - """Cross-platform table with box-drawing characters. On Windows it's double borders, on Linux/OSX it's unicode. - - :ivar iter table_data: List (empty or list of lists of strings) representing the table. - :ivar str title: Optional title to show within the top border of the table. - :ivar bool inner_column_border: Separates columns. - :ivar bool inner_footing_row_border: Show a border before the last row. - :ivar bool inner_heading_row_border: Show a border after the first row. - :ivar bool inner_row_border: Show a border in between every row. - :ivar bool outer_border: Show the top, left, right, and bottom border. - :ivar dict justify_columns: Horizontal justification. Keys are column indexes (int). Values are right/left/center. - :ivar int padding_left: Number of spaces to pad on the left side of every cell. - :ivar int padding_right: Number of spaces to pad on the right side of every cell. - """ - - pass - - -class PorcelainTable(AsciiTable): - """An AsciiTable stripped to a minimum. - - Meant to be machine passable and roughly follow format set by git --porcelain option (hence the name). - - :ivar iter table_data: List (empty or list of lists of strings) representing the table. - """ - - def __init__(self, table_data): - """Constructor. - - :param iter table_data: List (empty or list of lists of strings) representing the table. - """ - # Porcelain table won't support title since it has no outer birders. - super(PorcelainTable, self).__init__(table_data) - - # Removes outer border, and inner footing and header row borders. - self.inner_footing_row_border = False - self.inner_heading_row_border = False - self.outer_border = False diff --git a/terminaltables3/__init__.py b/terminaltables3/__init__.py new file mode 100644 index 0000000..f0b4858 --- /dev/null +++ b/terminaltables3/__init__.py @@ -0,0 +1,17 @@ +"""Generate simple tables in terminals from a nested list of strings. + +Use SingleTable or DoubleTable instead of AsciiTable for box-drawing characters. + +https://github.com/Robpol86/terminaltables3 +https://pypi.python.org/pypi/terminaltables3 +""" + +from terminaltables3.ascii_table import AsciiTable # noqa +from terminaltables3.github_table import GithubFlavoredMarkdownTable # noqa +from terminaltables3.other_tables import DoubleTable # noqa +from terminaltables3.other_tables import PorcelainTable # noqa +from terminaltables3.other_tables import SingleTable # noqa + +__author__ = "@Robpol86" +__license__ = "MIT" +__version__ = "3.1.0" diff --git a/terminaltables/ascii_table.py b/terminaltables3/ascii_table.py index 3623918..4630f74 100644 --- a/terminaltables/ascii_table.py +++ b/terminaltables3/ascii_table.py @@ -1,8 +1,12 @@ """AsciiTable is the main table class. To be inherited by other tables. Define convenience methods here.""" -from terminaltables.base_table import BaseTable -from terminaltables.terminal_io import terminal_size -from terminaltables.width_and_alignment import column_max_width, max_dimensions, table_width +from terminaltables3.base_table import BaseTable +from terminaltables3.terminal_io import terminal_size +from terminaltables3.width_and_alignment import ( + column_max_width, + max_dimensions, + table_width, +) class AsciiTable(BaseTable): @@ -20,7 +24,7 @@ class AsciiTable(BaseTable): :ivar int padding_right: Number of spaces to pad on the right side of every cell. """ - def column_max_width(self, column_number): + def column_max_width(self, column_number: int) -> int: """Return the maximum width of a column based on the current terminal width. :param int column_number: The column number to query. @@ -32,24 +36,28 @@ class AsciiTable(BaseTable): outer_border = 2 if self.outer_border else 0 inner_border = 1 if self.inner_column_border else 0 padding = self.padding_left + self.padding_right - return column_max_width(inner_widths, column_number, outer_border, inner_border, padding) + return column_max_width( + inner_widths, column_number, outer_border, inner_border, padding + ) @property - def column_widths(self): + def column_widths(self) -> list[int]: """Return a list of integers representing the widths of each table column without padding.""" if not self.table_data: - return list() + return [] return max_dimensions(self.table_data)[0] @property - def ok(self): # Too late to change API. # pylint: disable=invalid-name + def ok(self) -> bool: # Too late to change API. # pylint: disable=invalid-name """Return True if the table fits within the terminal width, False if the table breaks.""" return self.table_width <= terminal_size()[0] @property - def table_width(self): + def table_width(self) -> int: """Return the width of the table including padding and borders.""" - outer_widths = max_dimensions(self.table_data, self.padding_left, self.padding_right)[2] + outer_widths = max_dimensions( + self.table_data, self.padding_left, self.padding_right + )[2] outer_border = 2 if self.outer_border else 0 inner_border = 1 if self.inner_column_border else 0 return table_width(outer_widths, outer_border, inner_border) diff --git a/terminaltables/base_table.py b/terminaltables3/base_table.py index 281d5a3..4c375af 100644 --- a/terminaltables/base_table.py +++ b/terminaltables3/base_table.py @@ -1,10 +1,12 @@ """Base table class. Define just the bare minimum to build tables.""" -from terminaltables.build import build_border, build_row, flatten -from terminaltables.width_and_alignment import align_and_pad_cell, max_dimensions +from typing import Generator, Optional, Sequence, Tuple +from terminaltables3.build import build_border, build_row, flatten +from terminaltables3.width_and_alignment import align_and_pad_cell, max_dimensions -class BaseTable(object): + +class BaseTable: """Base table class. :ivar iter table_data: List (empty or list of lists of strings) representing the table. @@ -19,37 +21,39 @@ class BaseTable(object): :ivar int padding_right: Number of spaces to pad on the right side of every cell. """ - CHAR_F_INNER_HORIZONTAL = '-' - CHAR_F_INNER_INTERSECT = '+' - CHAR_F_INNER_VERTICAL = '|' - CHAR_F_OUTER_LEFT_INTERSECT = '+' - CHAR_F_OUTER_LEFT_VERTICAL = '|' - CHAR_F_OUTER_RIGHT_INTERSECT = '+' - CHAR_F_OUTER_RIGHT_VERTICAL = '|' - CHAR_H_INNER_HORIZONTAL = '-' - CHAR_H_INNER_INTERSECT = '+' - CHAR_H_INNER_VERTICAL = '|' - CHAR_H_OUTER_LEFT_INTERSECT = '+' - CHAR_H_OUTER_LEFT_VERTICAL = '|' - CHAR_H_OUTER_RIGHT_INTERSECT = '+' - CHAR_H_OUTER_RIGHT_VERTICAL = '|' - CHAR_INNER_HORIZONTAL = '-' - CHAR_INNER_INTERSECT = '+' - CHAR_INNER_VERTICAL = '|' - CHAR_OUTER_BOTTOM_HORIZONTAL = '-' - CHAR_OUTER_BOTTOM_INTERSECT = '+' - CHAR_OUTER_BOTTOM_LEFT = '+' - CHAR_OUTER_BOTTOM_RIGHT = '+' - CHAR_OUTER_LEFT_INTERSECT = '+' - CHAR_OUTER_LEFT_VERTICAL = '|' - CHAR_OUTER_RIGHT_INTERSECT = '+' - CHAR_OUTER_RIGHT_VERTICAL = '|' - CHAR_OUTER_TOP_HORIZONTAL = '-' - CHAR_OUTER_TOP_INTERSECT = '+' - CHAR_OUTER_TOP_LEFT = '+' - CHAR_OUTER_TOP_RIGHT = '+' - - def __init__(self, table_data, title=None): + CHAR_F_INNER_HORIZONTAL = "-" + CHAR_F_INNER_INTERSECT = "+" + CHAR_F_INNER_VERTICAL = "|" + CHAR_F_OUTER_LEFT_INTERSECT = "+" + CHAR_F_OUTER_LEFT_VERTICAL = "|" + CHAR_F_OUTER_RIGHT_INTERSECT = "+" + CHAR_F_OUTER_RIGHT_VERTICAL = "|" + CHAR_H_INNER_HORIZONTAL = "-" + CHAR_H_INNER_INTERSECT = "+" + CHAR_H_INNER_VERTICAL = "|" + CHAR_H_OUTER_LEFT_INTERSECT = "+" + CHAR_H_OUTER_LEFT_VERTICAL = "|" + CHAR_H_OUTER_RIGHT_INTERSECT = "+" + CHAR_H_OUTER_RIGHT_VERTICAL = "|" + CHAR_INNER_HORIZONTAL = "-" + CHAR_INNER_INTERSECT = "+" + CHAR_INNER_VERTICAL = "|" + CHAR_OUTER_BOTTOM_HORIZONTAL = "-" + CHAR_OUTER_BOTTOM_INTERSECT = "+" + CHAR_OUTER_BOTTOM_LEFT = "+" + CHAR_OUTER_BOTTOM_RIGHT = "+" + CHAR_OUTER_LEFT_INTERSECT = "+" + CHAR_OUTER_LEFT_VERTICAL = "|" + CHAR_OUTER_RIGHT_INTERSECT = "+" + CHAR_OUTER_RIGHT_VERTICAL = "|" + CHAR_OUTER_TOP_HORIZONTAL = "-" + CHAR_OUTER_TOP_INTERSECT = "+" + CHAR_OUTER_TOP_LEFT = "+" + CHAR_OUTER_TOP_RIGHT = "+" + + def __init__( + self, table_data: Sequence[Sequence[str]], title: Optional[str] = None + ): """Constructor. :param iter table_data: List (empty or list of lists of strings) representing the table. @@ -64,11 +68,13 @@ class BaseTable(object): self.inner_row_border = False self.outer_border = True - self.justify_columns = dict() # {0: 'right', 1: 'left', 2: 'center'} + self.justify_columns = {} # {0: 'right', 1: 'left', 2: 'center'} self.padding_left = 1 self.padding_right = 1 - def horizontal_border(self, style, outer_widths): + def horizontal_border( + self, style: str, outer_widths: Sequence[int] + ) -> Tuple[str, ...]: """Build any kind of horizontal border for the table. :param str style: Type of border to return. @@ -77,39 +83,45 @@ class BaseTable(object): :return: Prepared border as a tuple of strings. :rtype: tuple """ - if style == 'top': + if style == "top": horizontal = self.CHAR_OUTER_TOP_HORIZONTAL left = self.CHAR_OUTER_TOP_LEFT - intersect = self.CHAR_OUTER_TOP_INTERSECT if self.inner_column_border else '' + intersect = ( + self.CHAR_OUTER_TOP_INTERSECT if self.inner_column_border else "" + ) right = self.CHAR_OUTER_TOP_RIGHT title = self.title - elif style == 'bottom': + elif style == "bottom": horizontal = self.CHAR_OUTER_BOTTOM_HORIZONTAL left = self.CHAR_OUTER_BOTTOM_LEFT - intersect = self.CHAR_OUTER_BOTTOM_INTERSECT if self.inner_column_border else '' + intersect = ( + self.CHAR_OUTER_BOTTOM_INTERSECT if self.inner_column_border else "" + ) right = self.CHAR_OUTER_BOTTOM_RIGHT title = None - elif style == 'heading': + elif style == "heading": horizontal = self.CHAR_H_INNER_HORIZONTAL - left = self.CHAR_H_OUTER_LEFT_INTERSECT if self.outer_border else '' - intersect = self.CHAR_H_INNER_INTERSECT if self.inner_column_border else '' - right = self.CHAR_H_OUTER_RIGHT_INTERSECT if self.outer_border else '' + left = self.CHAR_H_OUTER_LEFT_INTERSECT if self.outer_border else "" + intersect = self.CHAR_H_INNER_INTERSECT if self.inner_column_border else "" + right = self.CHAR_H_OUTER_RIGHT_INTERSECT if self.outer_border else "" title = None - elif style == 'footing': + elif style == "footing": horizontal = self.CHAR_F_INNER_HORIZONTAL - left = self.CHAR_F_OUTER_LEFT_INTERSECT if self.outer_border else '' - intersect = self.CHAR_F_INNER_INTERSECT if self.inner_column_border else '' - right = self.CHAR_F_OUTER_RIGHT_INTERSECT if self.outer_border else '' + left = self.CHAR_F_OUTER_LEFT_INTERSECT if self.outer_border else "" + intersect = self.CHAR_F_INNER_INTERSECT if self.inner_column_border else "" + right = self.CHAR_F_OUTER_RIGHT_INTERSECT if self.outer_border else "" title = None else: horizontal = self.CHAR_INNER_HORIZONTAL - left = self.CHAR_OUTER_LEFT_INTERSECT if self.outer_border else '' - intersect = self.CHAR_INNER_INTERSECT if self.inner_column_border else '' - right = self.CHAR_OUTER_RIGHT_INTERSECT if self.outer_border else '' + left = self.CHAR_OUTER_LEFT_INTERSECT if self.outer_border else "" + intersect = self.CHAR_INNER_INTERSECT if self.inner_column_border else "" + right = self.CHAR_OUTER_RIGHT_INTERSECT if self.outer_border else "" title = None return build_border(outer_widths, horizontal, left, intersect, right, title) - def gen_row_lines(self, row, style, inner_widths, height): + def gen_row_lines( + self, row: Sequence[str], style: str, inner_widths: Sequence[int], height: int + ) -> Generator[Tuple[str, ...], None, None]: r"""Combine cells in row and group them into lines with vertical borders. Caller is expected to pass yielded lines to ''.join() to combine them into a printable line. Caller must append @@ -137,38 +149,44 @@ class BaseTable(object): :return: Yields lines split into components in a list. Caller must ''.join() line. """ - cells_in_row = list() + cells_in_row = [] # Resize row if it doesn't have enough cells. if len(row) != len(inner_widths): - row = row + [''] * (len(inner_widths) - len(row)) + row = row + [""] * (len(inner_widths) - len(row)) # Pad and align each cell. Split each cell into lines to support multi-line cells. for i, cell in enumerate(row): align = (self.justify_columns.get(i),) inner_dimensions = (inner_widths[i], height) padding = (self.padding_left, self.padding_right, 0, 0) - cells_in_row.append(align_and_pad_cell(cell, align, inner_dimensions, padding)) + cells_in_row.append( + align_and_pad_cell(cell, align, inner_dimensions, padding) + ) # Determine border characters. - if style == 'heading': - left = self.CHAR_H_OUTER_LEFT_VERTICAL if self.outer_border else '' - center = self.CHAR_H_INNER_VERTICAL if self.inner_column_border else '' - right = self.CHAR_H_OUTER_RIGHT_VERTICAL if self.outer_border else '' - elif style == 'footing': - left = self.CHAR_F_OUTER_LEFT_VERTICAL if self.outer_border else '' - center = self.CHAR_F_INNER_VERTICAL if self.inner_column_border else '' - right = self.CHAR_F_OUTER_RIGHT_VERTICAL if self.outer_border else '' + if style == "heading": + left = self.CHAR_H_OUTER_LEFT_VERTICAL if self.outer_border else "" + center = self.CHAR_H_INNER_VERTICAL if self.inner_column_border else "" + right = self.CHAR_H_OUTER_RIGHT_VERTICAL if self.outer_border else "" + elif style == "footing": + left = self.CHAR_F_OUTER_LEFT_VERTICAL if self.outer_border else "" + center = self.CHAR_F_INNER_VERTICAL if self.inner_column_border else "" + right = self.CHAR_F_OUTER_RIGHT_VERTICAL if self.outer_border else "" else: - left = self.CHAR_OUTER_LEFT_VERTICAL if self.outer_border else '' - center = self.CHAR_INNER_VERTICAL if self.inner_column_border else '' - right = self.CHAR_OUTER_RIGHT_VERTICAL if self.outer_border else '' + left = self.CHAR_OUTER_LEFT_VERTICAL if self.outer_border else "" + center = self.CHAR_INNER_VERTICAL if self.inner_column_border else "" + right = self.CHAR_OUTER_RIGHT_VERTICAL if self.outer_border else "" # Yield each line. - for line in build_row(cells_in_row, left, center, right): - yield line - - def gen_table(self, inner_widths, inner_heights, outer_widths): + yield from build_row(cells_in_row, left, center, right) + + def gen_table( + self, + inner_widths: Sequence[int], + inner_heights: Sequence[int], + outer_widths: Sequence[int], + ) -> Generator[Tuple[str, ...], None, None]: """Combine everything and yield every line of the entire table with borders. :param iter inner_widths: List of widths (no padding) for each column. @@ -178,7 +196,7 @@ class BaseTable(object): """ # Yield top border. if self.outer_border: - yield self.horizontal_border('top', outer_widths) + yield self.horizontal_border("top", outer_widths) # Yield table body. row_count = len(self.table_data) @@ -186,32 +204,33 @@ class BaseTable(object): for i, row in enumerate(self.table_data): # Yield the row line by line (e.g. multi-line rows). if self.inner_heading_row_border and i == 0: - style = 'heading' + style = "heading" elif self.inner_footing_row_border and i == last_row_index: - style = 'footing' + style = "footing" else: - style = 'row' - for line in self.gen_row_lines(row, style, inner_widths, inner_heights[i]): - yield line + style = "row" + yield from self.gen_row_lines(row, style, inner_widths, inner_heights[i]) # If this is the last row then break. No separator needed. if i == last_row_index: break # Yield heading separator. if self.inner_heading_row_border and i == 0: - yield self.horizontal_border('heading', outer_widths) + yield self.horizontal_border("heading", outer_widths) # Yield footing separator. elif self.inner_footing_row_border and i == before_last_row_index: - yield self.horizontal_border('footing', outer_widths) + yield self.horizontal_border("footing", outer_widths) # Yield row separator. elif self.inner_row_border: - yield self.horizontal_border('row', outer_widths) + yield self.horizontal_border("row", outer_widths) # Yield bottom border. if self.outer_border: - yield self.horizontal_border('bottom', outer_widths) + yield self.horizontal_border("bottom", outer_widths) @property - def table(self): + def table(self) -> str: """Return a large string of the entire table ready to be printed to the terminal.""" - dimensions = max_dimensions(self.table_data, self.padding_left, self.padding_right)[:3] + dimensions = max_dimensions( + self.table_data, self.padding_left, self.padding_right + )[:3] return flatten(self.gen_table(*dimensions)) diff --git a/terminaltables/build.py b/terminaltables3/build.py index 6b23b2f..c01349f 100644 --- a/terminaltables/build.py +++ b/terminaltables3/build.py @@ -1,9 +1,18 @@ """Combine cells into rows.""" -from terminaltables.width_and_alignment import visible_width +from typing import Generator, Iterator, Optional, Sequence, Union +from terminaltables3.width_and_alignment import visible_width -def combine(line, left, intersect, right): + +def combine( + line: Union[ + Generator[Union[int, str], None, None], Iterator[Optional[Union[int, str]]] + ], + left: str, + intersect: Optional[str], + right: str, +) -> Generator[int, None, None]: """Zip borders between items in `line`. e.g. ('l', '1', 'c', '2', 'c', '3', 'r') @@ -41,15 +50,21 @@ def combine(line, left, intersect, right): yield intersect item = peek else: - for i in line: - yield i + yield from line - # Yield right border. + # Yield right border. if right: yield right -def build_border(outer_widths, horizontal, left, intersect, right, title=None): +def build_border( + outer_widths: Sequence[int], + horizontal: str, + left: str, + intersect: str, + right: str, + title: Optional[str] = None, +): """Build the top/bottom/middle row. Optionally embed the table title within the border. Title is hidden if it doesn't fit between the left/right characters/edges. @@ -86,9 +101,13 @@ def build_border(outer_widths, horizontal, left, intersect, right, title=None): # Handle title fitting in the first column. if length == outer_widths[0]: - return combine([title] + [horizontal * c for c in outer_widths[1:]], left, intersect, right) + return combine( + [title] + [horizontal * c for c in outer_widths[1:]], left, intersect, right + ) if length < outer_widths[0]: - columns = [title + horizontal * (outer_widths[0] - length)] + [horizontal * c for c in outer_widths[1:]] + columns = [title + horizontal * (outer_widths[0] - length)] + [ + horizontal * c for c in outer_widths[1:] + ] return combine(columns, left, intersect, right) # Handle wide titles/narrow columns. @@ -96,7 +115,9 @@ def build_border(outer_widths, horizontal, left, intersect, right, title=None): for width in combine(outer_widths, None, bool(intersect), None): # If title is taken care of. if length < 1: - columns_and_intersects.append(intersect if width is True else horizontal * width) + columns_and_intersects.append( + intersect if width is True else horizontal * width + ) # If title's last character overrides an intersect character. elif width is True and length == 1: length = 0 @@ -105,7 +126,9 @@ def build_border(outer_widths, horizontal, left, intersect, right, title=None): length -= 1 # If title's last character is within a column. elif width >= length: - columns_and_intersects[0] += horizontal * (width - length) # Append horizontal chars to title. + columns_and_intersects[0] += horizontal * ( + width - length + ) # Append horizontal chars to title. length = 0 # If remainder of title won't fit in a column. else: @@ -148,4 +171,4 @@ def flatten(table): :return: Joined rows/cells. :rtype: str """ - return '\n'.join(''.join(r) for r in table) + return "\n".join("".join(r) for r in table) diff --git a/terminaltables/github_table.py b/terminaltables3/github_table.py index 7eb1be7..a00d189 100644 --- a/terminaltables/github_table.py +++ b/terminaltables3/github_table.py @@ -1,7 +1,9 @@ """GithubFlavoredMarkdownTable class.""" -from terminaltables.ascii_table import AsciiTable -from terminaltables.build import combine +from typing import Sequence + +from terminaltables3.ascii_table import AsciiTable +from terminaltables3.build import combine class GithubFlavoredMarkdownTable(AsciiTable): @@ -13,13 +15,13 @@ class GithubFlavoredMarkdownTable(AsciiTable): :ivar dict justify_columns: Horizontal justification. Keys are column indexes (int). Values are right/left/center. """ - def __init__(self, table_data): + def __init__(self, table_data: Sequence[Sequence[str]]): """Constructor. :param iter table_data: List (empty or list of lists of strings) representing the table. """ # Github flavored markdown table won't support title. - super(GithubFlavoredMarkdownTable, self).__init__(table_data) + super().__init__(table_data) def horizontal_border(self, _, outer_widths): """Handle the GitHub heading border. @@ -38,16 +40,18 @@ class GithubFlavoredMarkdownTable(AsciiTable): intersect = self.CHAR_INNER_VERTICAL right = self.CHAR_OUTER_RIGHT_VERTICAL - columns = list() + columns = [] for i, width in enumerate(outer_widths): justify = self.justify_columns.get(i) - width = max(3, width) # Width should be at least 3 so justification can be applied. - if justify == 'left': - columns.append(':' + horizontal * (width - 1)) - elif justify == 'right': - columns.append(horizontal * (width - 1) + ':') - elif justify == 'center': - columns.append(':' + horizontal * (width - 2) + ':') + width = max( + 3, width + ) # Width should be at least 3 so justification can be applied. + if justify == "left": + columns.append(":" + horizontal * (width - 1)) + elif justify == "right": + columns.append(horizontal * (width - 1) + ":") + elif justify == "center": + columns.append(":" + horizontal * (width - 2) + ":") else: columns.append(horizontal * width) @@ -63,8 +67,7 @@ class GithubFlavoredMarkdownTable(AsciiTable): """ for i, row in enumerate(self.table_data): # Yield the row line by line (e.g. multi-line rows). - for line in self.gen_row_lines(row, 'row', inner_widths, inner_heights[i]): - yield line + yield from self.gen_row_lines(row, "row", inner_widths, inner_heights[i]) # Yield heading separator. if i == 0: yield self.horizontal_border(None, outer_widths) diff --git a/terminaltables3/other_tables.py b/terminaltables3/other_tables.py new file mode 100644 index 0000000..9fb1b05 --- /dev/null +++ b/terminaltables3/other_tables.py @@ -0,0 +1,173 @@ +"""Additional simple tables defined here.""" + +from terminaltables3.ascii_table import AsciiTable +from terminaltables3.terminal_io import IS_WINDOWS + + +class UnixTable(AsciiTable): + """Draw a table using box-drawing characters on Unix platforms. Table borders won't have any gaps between lines. + + Similar to the tables shown on PC BIOS boot messages, but not double-lined. + """ + + CHAR_F_INNER_HORIZONTAL = "\033(0\x71\033(B" + CHAR_F_INNER_INTERSECT = "\033(0\x6e\033(B" + CHAR_F_INNER_VERTICAL = "\033(0\x78\033(B" + CHAR_F_OUTER_LEFT_INTERSECT = "\033(0\x74\033(B" + CHAR_F_OUTER_LEFT_VERTICAL = "\033(0\x78\033(B" + CHAR_F_OUTER_RIGHT_INTERSECT = "\033(0\x75\033(B" + CHAR_F_OUTER_RIGHT_VERTICAL = "\033(0\x78\033(B" + CHAR_H_INNER_HORIZONTAL = "\033(0\x71\033(B" + CHAR_H_INNER_INTERSECT = "\033(0\x6e\033(B" + CHAR_H_INNER_VERTICAL = "\033(0\x78\033(B" + CHAR_H_OUTER_LEFT_INTERSECT = "\033(0\x74\033(B" + CHAR_H_OUTER_LEFT_VERTICAL = "\033(0\x78\033(B" + CHAR_H_OUTER_RIGHT_INTERSECT = "\033(0\x75\033(B" + CHAR_H_OUTER_RIGHT_VERTICAL = "\033(0\x78\033(B" + CHAR_INNER_HORIZONTAL = "\033(0\x71\033(B" + CHAR_INNER_INTERSECT = "\033(0\x6e\033(B" + CHAR_INNER_VERTICAL = "\033(0\x78\033(B" + CHAR_OUTER_BOTTOM_HORIZONTAL = "\033(0\x71\033(B" + CHAR_OUTER_BOTTOM_INTERSECT = "\033(0\x76\033(B" + CHAR_OUTER_BOTTOM_LEFT = "\033(0\x6d\033(B" + CHAR_OUTER_BOTTOM_RIGHT = "\033(0\x6a\033(B" + CHAR_OUTER_LEFT_INTERSECT = "\033(0\x74\033(B" + CHAR_OUTER_LEFT_VERTICAL = "\033(0\x78\033(B" + CHAR_OUTER_RIGHT_INTERSECT = "\033(0\x75\033(B" + CHAR_OUTER_RIGHT_VERTICAL = "\033(0\x78\033(B" + CHAR_OUTER_TOP_HORIZONTAL = "\033(0\x71\033(B" + CHAR_OUTER_TOP_INTERSECT = "\033(0\x77\033(B" + CHAR_OUTER_TOP_LEFT = "\033(0\x6c\033(B" + CHAR_OUTER_TOP_RIGHT = "\033(0\x6b\033(B" + + @property + def table(self): + """Return a large string of the entire table ready to be printed to the terminal.""" + ascii_table = super().table + optimized = ascii_table.replace("\033(B\033(0", "") + return optimized + + +class WindowsTable(AsciiTable): + """Draw a table using box-drawing characters on Windows platforms. This uses Code Page 437. Single-line borders. + + From: http://en.wikipedia.org/wiki/Code_page_437#Characters + """ + + CHAR_F_INNER_HORIZONTAL = b"\xc4".decode("ibm437") + CHAR_F_INNER_INTERSECT = b"\xc5".decode("ibm437") + CHAR_F_INNER_VERTICAL = b"\xb3".decode("ibm437") + CHAR_F_OUTER_LEFT_INTERSECT = b"\xc3".decode("ibm437") + CHAR_F_OUTER_LEFT_VERTICAL = b"\xb3".decode("ibm437") + CHAR_F_OUTER_RIGHT_INTERSECT = b"\xb4".decode("ibm437") + CHAR_F_OUTER_RIGHT_VERTICAL = b"\xb3".decode("ibm437") + CHAR_H_INNER_HORIZONTAL = b"\xc4".decode("ibm437") + CHAR_H_INNER_INTERSECT = b"\xc5".decode("ibm437") + CHAR_H_INNER_VERTICAL = b"\xb3".decode("ibm437") + CHAR_H_OUTER_LEFT_INTERSECT = b"\xc3".decode("ibm437") + CHAR_H_OUTER_LEFT_VERTICAL = b"\xb3".decode("ibm437") + CHAR_H_OUTER_RIGHT_INTERSECT = b"\xb4".decode("ibm437") + CHAR_H_OUTER_RIGHT_VERTICAL = b"\xb3".decode("ibm437") + CHAR_INNER_HORIZONTAL = b"\xc4".decode("ibm437") + CHAR_INNER_INTERSECT = b"\xc5".decode("ibm437") + CHAR_INNER_VERTICAL = b"\xb3".decode("ibm437") + CHAR_OUTER_BOTTOM_HORIZONTAL = b"\xc4".decode("ibm437") + CHAR_OUTER_BOTTOM_INTERSECT = b"\xc1".decode("ibm437") + CHAR_OUTER_BOTTOM_LEFT = b"\xc0".decode("ibm437") + CHAR_OUTER_BOTTOM_RIGHT = b"\xd9".decode("ibm437") + CHAR_OUTER_LEFT_INTERSECT = b"\xc3".decode("ibm437") + CHAR_OUTER_LEFT_VERTICAL = b"\xb3".decode("ibm437") + CHAR_OUTER_RIGHT_INTERSECT = b"\xb4".decode("ibm437") + CHAR_OUTER_RIGHT_VERTICAL = b"\xb3".decode("ibm437") + CHAR_OUTER_TOP_HORIZONTAL = b"\xc4".decode("ibm437") + CHAR_OUTER_TOP_INTERSECT = b"\xc2".decode("ibm437") + CHAR_OUTER_TOP_LEFT = b"\xda".decode("ibm437") + CHAR_OUTER_TOP_RIGHT = b"\xbf".decode("ibm437") + + +class WindowsTableDouble(AsciiTable): + """Draw a table using box-drawing characters on Windows platforms. This uses Code Page 437. Double-line borders.""" + + CHAR_F_INNER_HORIZONTAL = b"\xcd".decode("ibm437") + CHAR_F_INNER_INTERSECT = b"\xce".decode("ibm437") + CHAR_F_INNER_VERTICAL = b"\xba".decode("ibm437") + CHAR_F_OUTER_LEFT_INTERSECT = b"\xcc".decode("ibm437") + CHAR_F_OUTER_LEFT_VERTICAL = b"\xba".decode("ibm437") + CHAR_F_OUTER_RIGHT_INTERSECT = b"\xb9".decode("ibm437") + CHAR_F_OUTER_RIGHT_VERTICAL = b"\xba".decode("ibm437") + CHAR_H_INNER_HORIZONTAL = b"\xcd".decode("ibm437") + CHAR_H_INNER_INTERSECT = b"\xce".decode("ibm437") + CHAR_H_INNER_VERTICAL = b"\xba".decode("ibm437") + CHAR_H_OUTER_LEFT_INTERSECT = b"\xcc".decode("ibm437") + CHAR_H_OUTER_LEFT_VERTICAL = b"\xba".decode("ibm437") + CHAR_H_OUTER_RIGHT_INTERSECT = b"\xb9".decode("ibm437") + CHAR_H_OUTER_RIGHT_VERTICAL = b"\xba".decode("ibm437") + CHAR_INNER_HORIZONTAL = b"\xcd".decode("ibm437") + CHAR_INNER_INTERSECT = b"\xce".decode("ibm437") + CHAR_INNER_VERTICAL = b"\xba".decode("ibm437") + CHAR_OUTER_BOTTOM_HORIZONTAL = b"\xcd".decode("ibm437") + CHAR_OUTER_BOTTOM_INTERSECT = b"\xca".decode("ibm437") + CHAR_OUTER_BOTTOM_LEFT = b"\xc8".decode("ibm437") + CHAR_OUTER_BOTTOM_RIGHT = b"\xbc".decode("ibm437") + CHAR_OUTER_LEFT_INTERSECT = b"\xcc".decode("ibm437") + CHAR_OUTER_LEFT_VERTICAL = b"\xba".decode("ibm437") + CHAR_OUTER_RIGHT_INTERSECT = b"\xb9".decode("ibm437") + CHAR_OUTER_RIGHT_VERTICAL = b"\xba".decode("ibm437") + CHAR_OUTER_TOP_HORIZONTAL = b"\xcd".decode("ibm437") + CHAR_OUTER_TOP_INTERSECT = b"\xcb".decode("ibm437") + CHAR_OUTER_TOP_LEFT = b"\xc9".decode("ibm437") + CHAR_OUTER_TOP_RIGHT = b"\xbb".decode("ibm437") + + +class SingleTable(WindowsTable if IS_WINDOWS else UnixTable): + """Cross-platform table with single-line box-drawing characters. + + :ivar iter table_data: List (empty or list of lists of strings) representing the table. + :ivar str title: Optional title to show within the top border of the table. + :ivar bool inner_column_border: Separates columns. + :ivar bool inner_footing_row_border: Show a border before the last row. + :ivar bool inner_heading_row_border: Show a border after the first row. + :ivar bool inner_row_border: Show a border in between every row. + :ivar bool outer_border: Show the top, left, right, and bottom border. + :ivar dict justify_columns: Horizontal justification. Keys are column indexes (int). Values are right/left/center. + :ivar int padding_left: Number of spaces to pad on the left side of every cell. + :ivar int padding_right: Number of spaces to pad on the right side of every cell. + """ + + +class DoubleTable(WindowsTableDouble): + """Cross-platform table with box-drawing characters. On Windows it's double borders, on Linux/OSX it's unicode. + + :ivar iter table_data: List (empty or list of lists of strings) representing the table. + :ivar str title: Optional title to show within the top border of the table. + :ivar bool inner_column_border: Separates columns. + :ivar bool inner_footing_row_border: Show a border before the last row. + :ivar bool inner_heading_row_border: Show a border after the first row. + :ivar bool inner_row_border: Show a border in between every row. + :ivar bool outer_border: Show the top, left, right, and bottom border. + :ivar dict justify_columns: Horizontal justification. Keys are column indexes (int). Values are right/left/center. + :ivar int padding_left: Number of spaces to pad on the left side of every cell. + :ivar int padding_right: Number of spaces to pad on the right side of every cell. + """ + + +class PorcelainTable(AsciiTable): + """An AsciiTable stripped to a minimum. + + Meant to be machine passable and roughly follow format set by git --porcelain option (hence the name). + + :ivar iter table_data: List (empty or list of lists of strings) representing the table. + """ + + def __init__(self, table_data): + """Constructor. + + :param iter table_data: List (empty or list of lists of strings) representing the table. + """ + # Porcelain table won't support title since it has no outer birders. + super().__init__(table_data) + + # Removes outer border, and inner footing and header row borders. + self.inner_footing_row_border = False + self.inner_heading_row_border = False + self.outer_border = False diff --git a/terminaltables/terminal_io.py b/terminaltables3/terminal_io.py index 8b8c10d..dc04431 100644 --- a/terminaltables/terminal_io.py +++ b/terminaltables3/terminal_io.py @@ -3,16 +3,17 @@ import ctypes import struct import sys +from typing import Tuple, Union DEFAULT_HEIGHT = 24 DEFAULT_WIDTH = 79 INVALID_HANDLE_VALUE = -1 -IS_WINDOWS = sys.platform == 'win32' +IS_WINDOWS = sys.platform == "win32" STD_ERROR_HANDLE = -12 STD_OUTPUT_HANDLE = -11 -def get_console_info(kernel32, handle): +def get_console_info(kernel32, handle: int) -> Tuple[int, int]: """Get information about this current console window (Windows only). https://github.com/Robpol86/colorclass/blob/ab42da59/colorclass/windows.py#L111 @@ -26,7 +27,7 @@ def get_console_info(kernel32, handle): :rtype: tuple """ if handle == INVALID_HANDLE_VALUE: - raise OSError('Invalid handle.') + raise OSError("Invalid handle.") # Query Win32 API. lpcsbi = ctypes.create_string_buffer(22) # Populated by GetConsoleScreenBufferInfo. @@ -34,12 +35,12 @@ def get_console_info(kernel32, handle): raise ctypes.WinError() # Subclass of OSError. # Parse data. - left, top, right, bottom = struct.unpack('hhhhHhhhhhh', lpcsbi.raw)[5:-2] + left, top, right, bottom = struct.unpack("hhhhHhhhhhh", lpcsbi.raw)[5:-2] width, height = right - left, bottom - top return width, height -def terminal_size(kernel32=None): +def terminal_size(kernel32=None) -> Tuple[int, int]: """Get the width and height of the terminal. http://code.activestate.com/recipes/440694-determine-size-of-console-window-on-windows/ @@ -56,19 +57,23 @@ def terminal_size(kernel32=None): return get_console_info(kernel32, kernel32.GetStdHandle(STD_ERROR_HANDLE)) except OSError: try: - return get_console_info(kernel32, kernel32.GetStdHandle(STD_OUTPUT_HANDLE)) + return get_console_info( + kernel32, kernel32.GetStdHandle(STD_OUTPUT_HANDLE) + ) except OSError: return DEFAULT_WIDTH, DEFAULT_HEIGHT try: - device = __import__('fcntl').ioctl(0, __import__('termios').TIOCGWINSZ, '\0\0\0\0\0\0\0\0') - except IOError: + device = __import__("fcntl").ioctl( + 0, __import__("termios").TIOCGWINSZ, "\0\0\0\0\0\0\0\0" + ) + except OSError: return DEFAULT_WIDTH, DEFAULT_HEIGHT - height, width = struct.unpack('hhhh', device)[:2] + height, width = struct.unpack("hhhh", device)[:2] return width, height -def set_terminal_title(title, kernel32=None): +def set_terminal_title(title: Union[str, bytes], kernel32=None) -> bool: """Set the terminal title. :param title: The title to set (string, unicode, bytes accepted). @@ -78,7 +83,7 @@ def set_terminal_title(title, kernel32=None): :rtype: bool """ try: - title_bytes = title.encode('utf-8') + title_bytes = title.encode("utf-8") except AttributeError: title_bytes = title @@ -90,9 +95,8 @@ def set_terminal_title(title, kernel32=None): is_ascii = all(c < 128 for c in title) # bytes. if is_ascii: return kernel32.SetConsoleTitleA(title_bytes) != 0 - else: - return kernel32.SetConsoleTitleW(title) != 0 + return kernel32.SetConsoleTitleW(title) != 0 # Linux/OSX. - sys.stdout.write(b'\033]0;' + title_bytes + b'\007') + sys.stdout.write(b"\033]0;" + title_bytes + b"\007") return True diff --git a/terminaltables/width_and_alignment.py b/terminaltables3/width_and_alignment.py index 057e800..6f7d807 100644 --- a/terminaltables/width_and_alignment.py +++ b/terminaltables3/width_and_alignment.py @@ -2,36 +2,37 @@ import re import unicodedata +from typing import Sequence, Tuple -from terminaltables.terminal_io import terminal_size +from terminaltables3.terminal_io import terminal_size -RE_COLOR_ANSI = re.compile(r'(\033\[[\d;]+m)') +RE_COLOR_ANSI = re.compile(r"(\033\[[\d;]+m)") -def visible_width(string): +def visible_width(string: str) -> int: """Get the visible width of a unicode string. Some CJK unicode characters are more than one byte unlike ASCII and latin unicode characters. - From: https://github.com/Robpol86/terminaltables/pull/9 + From: https://github.com/Robpol86/terminaltables3/pull/9 :param str string: String to measure. :return: String's width. :rtype: int """ - if '\033' in string: - string = RE_COLOR_ANSI.sub('', string) + if "\033" in string: + string = RE_COLOR_ANSI.sub("", string) # Convert to unicode. try: - string = string.decode('u8') + string = string.decode("u8") except (AttributeError, UnicodeEncodeError): pass width = 0 for char in string: - if unicodedata.east_asian_width(char) in ('F', 'W'): + if unicodedata.east_asian_width(char) in ("F", "W"): width += 2 else: width += 1 @@ -39,7 +40,13 @@ def visible_width(string): return width -def align_and_pad_cell(string, align, inner_dimensions, padding, space=' '): +def align_and_pad_cell( + string: str, + align: Tuple, + inner_dimensions: Tuple, + padding: Sequence[int], + space: str = " ", +) -> list[str]: """Align a string horizontally and vertically. Also add additional padding in both dimensions. :param str string: Input string to operate on. @@ -51,37 +58,55 @@ def align_and_pad_cell(string, align, inner_dimensions, padding, space=' '): :return: Padded cell split into lines. :rtype: list """ - if not hasattr(string, 'splitlines'): + if not hasattr(string, "splitlines"): string = str(string) # Handle trailing newlines or empty strings, str.splitlines() does not satisfy. - lines = string.splitlines() or [''] - if string.endswith('\n'): - lines.append('') + lines = string.splitlines() or [""] + if string.endswith("\n"): + lines.append("") # Vertically align and pad. - if 'bottom' in align: - lines = ([''] * (inner_dimensions[1] - len(lines) + padding[2])) + lines + ([''] * padding[3]) - elif 'middle' in align: + if "bottom" in align: + lines = ( + ([""] * (inner_dimensions[1] - len(lines) + padding[2])) + + lines + + ([""] * padding[3]) + ) + elif "middle" in align: delta = inner_dimensions[1] - len(lines) - lines = ([''] * (delta // 2 + delta % 2 + padding[2])) + lines + ([''] * (delta // 2 + padding[3])) + lines = ( + ([""] * (delta // 2 + delta % 2 + padding[2])) + + lines + + ([""] * (delta // 2 + padding[3])) + ) else: - lines = ([''] * padding[2]) + lines + ([''] * (inner_dimensions[1] - len(lines) + padding[3])) + lines = ( + ([""] * padding[2]) + + lines + + ([""] * (inner_dimensions[1] - len(lines) + padding[3])) + ) # Horizontally align and pad. for i, line in enumerate(lines): new_width = inner_dimensions[0] + len(line) - visible_width(line) - if 'right' in align: + if "right" in align: lines[i] = line.rjust(padding[0] + new_width, space) + (space * padding[1]) - elif 'center' in align: - lines[i] = (space * padding[0]) + line.center(new_width, space) + (space * padding[1]) + elif "center" in align: + lines[i] = ( + (space * padding[0]) + + line.center(new_width, space) + + (space * padding[1]) + ) else: lines[i] = (space * padding[0]) + line.ljust(new_width + padding[1], space) return lines -def max_dimensions(table_data, padding_left=0, padding_right=0, padding_top=0, padding_bottom=0): +def max_dimensions( + table_data, padding_left=0, padding_right=0, padding_top=0, padding_bottom=0 +): """Get maximum widths of each column and maximum height of each row. :param iter table_data: List of list of strings (unmodified table data). @@ -99,12 +124,15 @@ def max_dimensions(table_data, padding_left=0, padding_right=0, padding_top=0, p # Find max width and heights. for j, row in enumerate(table_data): for i, cell in enumerate(row): - if not hasattr(cell, 'count') or not hasattr(cell, 'splitlines'): + if not hasattr(cell, "count") or not hasattr(cell, "splitlines"): cell = str(cell) if not cell: continue - inner_heights[j] = max(inner_heights[j], cell.count('\n') + 1) - inner_widths[i] = max(inner_widths[i], *[visible_width(l) for l in cell.splitlines()]) + inner_heights[j] = max(inner_heights[j], cell.count("\n") + 1) + inner_widths[i] = max( + inner_widths[i], + *[visible_width(the_line) for the_line in cell.splitlines()] + ) # Calculate with padding. outer_widths = [padding_left + i + padding_right for i in inner_widths] @@ -113,7 +141,13 @@ def max_dimensions(table_data, padding_left=0, padding_right=0, padding_top=0, p return inner_widths, inner_heights, outer_widths, outer_heights -def column_max_width(inner_widths, column_number, outer_border, inner_border, padding): +def column_max_width( + inner_widths: Sequence[int], + column_number: int, + outer_border: int, + inner_border: int, + padding: int, +) -> int: """Determine the maximum width of a column based on the current terminal width. :param iter inner_widths: List of widths (no padding) for each column. @@ -138,7 +172,9 @@ def column_max_width(inner_widths, column_number, outer_border, inner_border, pa return terminal_width - data_space - non_data_space -def table_width(outer_widths, outer_border, inner_border): +def table_width( + outer_widths: Sequence[int], outer_border: int, inner_border: int +) -> int: """Determine the width of the entire table including borders and padding. :param iter outer_widths: List of widths (with padding) for each column. diff --git a/tests/__init__.py b/tests/__init__.py index b91337b..7baf433 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -2,4 +2,4 @@ import py -PROJECT_ROOT = py.path.local(__file__).dirpath().join('..') +PROJECT_ROOT = py.path.local(__file__).dirpath().join("..") diff --git a/tests/screenshot.py b/tests/screenshot.py index 6ccb593..0786d48 100644 --- a/tests/screenshot.py +++ b/tests/screenshot.py @@ -14,7 +14,7 @@ except ImportError: from tests import PROJECT_ROOT -STARTF_USESHOWWINDOW = getattr(subprocess, 'STARTF_USESHOWWINDOW', 1) +STARTF_USESHOWWINDOW = getattr(subprocess, "STARTF_USESHOWWINDOW", 1) STILL_ACTIVE = 259 SW_MAXIMIZE = 3 @@ -23,24 +23,24 @@ class StartupInfo(ctypes.Structure): """STARTUPINFO structure.""" _fields_ = [ - ('cb', ctypes.c_ulong), - ('lpReserved', ctypes.c_char_p), - ('lpDesktop', ctypes.c_char_p), - ('lpTitle', ctypes.c_char_p), - ('dwX', ctypes.c_ulong), - ('dwY', ctypes.c_ulong), - ('dwXSize', ctypes.c_ulong), - ('dwYSize', ctypes.c_ulong), - ('dwXCountChars', ctypes.c_ulong), - ('dwYCountChars', ctypes.c_ulong), - ('dwFillAttribute', ctypes.c_ulong), - ('dwFlags', ctypes.c_ulong), - ('wShowWindow', ctypes.c_ushort), - ('cbReserved2', ctypes.c_ushort), - ('lpReserved2', ctypes.c_char_p), - ('hStdInput', ctypes.c_ulong), - ('hStdOutput', ctypes.c_ulong), - ('hStdError', ctypes.c_ulong), + ("cb", ctypes.c_ulong), + ("lpReserved", ctypes.c_char_p), + ("lpDesktop", ctypes.c_char_p), + ("lpTitle", ctypes.c_char_p), + ("dwX", ctypes.c_ulong), + ("dwY", ctypes.c_ulong), + ("dwXSize", ctypes.c_ulong), + ("dwYSize", ctypes.c_ulong), + ("dwXCountChars", ctypes.c_ulong), + ("dwYCountChars", ctypes.c_ulong), + ("dwFillAttribute", ctypes.c_ulong), + ("dwFlags", ctypes.c_ulong), + ("wShowWindow", ctypes.c_ushort), + ("cbReserved2", ctypes.c_ushort), + ("lpReserved2", ctypes.c_char_p), + ("hStdInput", ctypes.c_ulong), + ("hStdOutput", ctypes.c_ulong), + ("hStdError", ctypes.c_ulong), ] def __init__(self, maximize=False, title=None): @@ -49,7 +49,7 @@ class StartupInfo(ctypes.Structure): :param bool maximize: Start process in new console window, maximized. :param bytes title: Set new window title to this instead of exe path. """ - super(StartupInfo, self).__init__() + super().__init__() self.cb = ctypes.sizeof(self) if maximize: self.dwFlags |= STARTF_USESHOWWINDOW @@ -62,14 +62,14 @@ class ProcessInfo(ctypes.Structure): """PROCESS_INFORMATION structure.""" _fields_ = [ - ('hProcess', ctypes.c_void_p), - ('hThread', ctypes.c_void_p), - ('dwProcessId', ctypes.c_ulong), - ('dwThreadId', ctypes.c_ulong), + ("hProcess", ctypes.c_void_p), + ("hThread", ctypes.c_void_p), + ("dwProcessId", ctypes.c_ulong), + ("dwThreadId", ctypes.c_ulong), ] -class RunNewConsole(object): +class RunNewConsole: """Run the command in a new console window. Windows only. Use in a with statement. subprocess sucks and really limits your access to the win32 API. Its implementation is half-assed. Using this so @@ -84,20 +84,27 @@ class RunNewConsole(object): :param bytes title: Set new window title to this. Needed by user32.FindWindow. """ if title is None: - title = 'pytest-{0}-{1}'.format(os.getpid(), random.randint(1000, 9999)).encode('ascii') + title = "pytest-{}-{}".format( + os.getpid(), random.randint(1000, 9999) + ).encode("ascii") self.startup_info = StartupInfo(maximize=maximized, title=title) self.process_info = ProcessInfo() - self.command_str = subprocess.list2cmdline(command).encode('ascii') - self._handles = list() + self.command_str = subprocess.list2cmdline(command).encode("ascii") + self._handles = [] self._kernel32 = ctypes.LibraryLoader(ctypes.WinDLL).kernel32 - self._kernel32.GetExitCodeProcess.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_ulong)] + self._kernel32.GetExitCodeProcess.argtypes = [ + ctypes.c_void_p, + ctypes.POINTER(ctypes.c_ulong), + ] self._kernel32.GetExitCodeProcess.restype = ctypes.c_long def __del__(self): """Close win32 handles.""" while self._handles: try: - self._kernel32.CloseHandle(self._handles.pop(0)) # .pop() is thread safe. + self._kernel32.CloseHandle( + self._handles.pop(0) + ) # .pop() is thread safe. except IndexError: break @@ -111,9 +118,9 @@ class RunNewConsole(object): False, # bInheritHandles subprocess.CREATE_NEW_CONSOLE, # dwCreationFlags None, # lpEnvironment - str(PROJECT_ROOT).encode('ascii'), # lpCurrentDirectory + str(PROJECT_ROOT).encode("ascii"), # lpCurrentDirectory ctypes.byref(self.startup_info), # lpStartupInfo - ctypes.byref(self.process_info) # lpProcessInformation + ctypes.byref(self.process_info), # lpProcessInformation ): raise ctypes.WinError() @@ -125,7 +132,9 @@ class RunNewConsole(object): self.hwnd = 0 for _ in range(int(5 / 0.1)): # Takes time for console window to initialize. - self.hwnd = ctypes.windll.user32.FindWindowA(None, self.startup_info.lpTitle) + self.hwnd = ctypes.windll.user32.FindWindowA( + None, self.startup_info.lpTitle + ) if self.hwnd: break time.sleep(0.1) @@ -141,7 +150,9 @@ class RunNewConsole(object): status = ctypes.c_ulong(STILL_ACTIVE) while status.value == STILL_ACTIVE: time.sleep(0.1) - if not self._kernel32.GetExitCodeProcess(self.process_info.hProcess, ctypes.byref(status)): + if not self._kernel32.GetExitCodeProcess( + self.process_info.hProcess, ctypes.byref(status) + ): raise ctypes.WinError() assert status.value == 0 finally: @@ -154,9 +165,11 @@ class RunNewConsole(object): :return: Yields region the new window is in (left, upper, right, lower). :rtype: tuple """ - rect = ctypes.create_string_buffer(16) # To be written to by GetWindowRect. RECT structure. + rect = ctypes.create_string_buffer( + 16 + ) # To be written to by GetWindowRect. RECT structure. while ctypes.windll.user32.GetWindowRect(self.hwnd, rect): - left, top, right, bottom = struct.unpack('llll', rect.raw) + left, top, right, bottom = struct.unpack("llll", rect.raw) width, height = right - left, bottom - top assert width > 1 assert height > 1 @@ -179,8 +192,7 @@ def iter_rows(pil_image): :rtype: tuple """ iterator = izip(*(iter(pil_image.getdata()),) * pil_image.width) - for row in iterator: - yield row + yield from iterator def get_most_interesting_row(pil_image): @@ -224,11 +236,13 @@ def count_subimages(screenshot, subimg): for x_pos in range(screenshot.width - si_width + 1): if row[x_pos] != si_pixel: continue # First pixel does not match. - if row[x_pos:x_pos + si_width] != si_row: + if row[x_pos : x_pos + si_width] != si_row: continue # Row does not match. # Found match for interesting row of subimg in screenshot. y_corrected = y_pos - si_y - with screenshot.crop((x_pos, y_corrected, x_pos + si_width, y_corrected + si_height)) as cropped: + with screenshot.crop( + (x_pos, y_corrected, x_pos + si_width, y_corrected + si_height) + ) as cropped: if list(cropped.getdata()) == si_pixels: occurrences += 1 @@ -248,11 +262,12 @@ def try_candidates(screenshot, subimg_candidates, expected_count): :rtype: int """ from PIL import Image + count_found = 0 for subimg_path in subimg_candidates: with Image.open(subimg_path) as rgba_s: - with rgba_s.convert(mode='RGB') as subimg: + with rgba_s.convert(mode="RGB") as subimg: # Make sure subimage isn't too large. assert subimg.width < 256 assert subimg.height < 256 @@ -277,14 +292,17 @@ def screenshot_until_match(save_to, timeout, subimg_candidates, expected_count, :param iter gen: Generator yielding window position and size to crop screenshot to. """ from PIL import ImageGrab - assert save_to.endswith('.png') + + assert save_to.endswith(".png") stop_after = time.time() + timeout # Take screenshots until subimage is found. while True: with ImageGrab.grab(next(gen)) as rgba: - with rgba.convert(mode='RGB') as screenshot: - count_found = try_candidates(screenshot, subimg_candidates, expected_count) + with rgba.convert(mode="RGB") as screenshot: + count_found = try_candidates( + screenshot, subimg_candidates, expected_count + ) if count_found == expected_count or time.time() > stop_after: screenshot.save(save_to) assert count_found == expected_count diff --git a/tests/test_all_tables_e2e/test_ascii_table.py b/tests/test_all_tables_e2e/test_ascii_table.py index a1a3b9c..ded2bce 100644 --- a/tests/test_all_tables_e2e/test_ascii_table.py +++ b/tests/test_all_tables_e2e/test_ascii_table.py @@ -6,8 +6,8 @@ from textwrap import dedent import py import pytest -from terminaltables import AsciiTable -from terminaltables.terminal_io import IS_WINDOWS +from terminaltables3 import AsciiTable +from terminaltables3.terminal_io import IS_WINDOWS from tests import PROJECT_ROOT from tests.screenshot import RunNewConsole, screenshot_until_match @@ -17,31 +17,31 @@ HERE = py.path.local(__file__).dirpath() def test_single_line(): """Test single-lined cells.""" table_data = [ - ['Name', 'Color', 'Type'], - ['Avocado', 'green', 'nut'], - ['Tomato', 'red', 'fruit'], - ['Lettuce', 'green', 'vegetable'], - ['Watermelon', 'green'], + ["Name", "Color", "Type"], + ["Avocado", "green", "nut"], + ["Tomato", "red", "fruit"], + ["Lettuce", "green", "vegetable"], + ["Watermelon", "green"], [], ] - table = AsciiTable(table_data, 'Example') + table = AsciiTable(table_data, "Example") table.inner_footing_row_border = True - table.justify_columns[0] = 'left' - table.justify_columns[1] = 'center' - table.justify_columns[2] = 'right' + table.justify_columns[0] = "left" + table.justify_columns[1] = "center" + table.justify_columns[2] = "right" actual = table.table expected = ( - '+Example-----+-------+-----------+\n' - '| Name | Color | Type |\n' - '+------------+-------+-----------+\n' - '| Avocado | green | nut |\n' - '| Tomato | red | fruit |\n' - '| Lettuce | green | vegetable |\n' - '| Watermelon | green | |\n' - '+------------+-------+-----------+\n' - '| | | |\n' - '+------------+-------+-----------+' + "+Example-----+-------+-----------+\n" + "| Name | Color | Type |\n" + "+------------+-------+-----------+\n" + "| Avocado | green | nut |\n" + "| Tomato | red | fruit |\n" + "| Lettuce | green | vegetable |\n" + "| Watermelon | green | |\n" + "+------------+-------+-----------+\n" + "| | | |\n" + "+------------+-------+-----------+" ) assert actual == expected @@ -49,22 +49,25 @@ def test_single_line(): def test_multi_line(): """Test multi-lined cells.""" table_data = [ - ['Show', 'Characters'], - ['Rugrats', 'Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles'], - ['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] + ["Show", "Characters"], + [ + "Rugrats", + "Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles", + ], + ["South Park", "Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick"], ] table = AsciiTable(table_data) # Test defaults. actual = table.table expected = ( - '+------------+-------------------------------------------------------------------------------------+\n' - '| Show | Characters |\n' - '+------------+-------------------------------------------------------------------------------------+\n' - '| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n' - '| | Dil Pickles |\n' - '| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |\n' - '+------------+-------------------------------------------------------------------------------------+' + "+------------+-------------------------------------------------------------------------------------+\n" + "| Show | Characters |\n" + "+------------+-------------------------------------------------------------------------------------+\n" + "| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n" + "| | Dil Pickles |\n" + "| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |\n" + "+------------+-------------------------------------------------------------------------------------+" ) assert actual == expected @@ -72,52 +75,53 @@ def test_multi_line(): table.inner_row_border = True actual = table.table expected = ( - '+------------+-------------------------------------------------------------------------------------+\n' - '| Show | Characters |\n' - '+------------+-------------------------------------------------------------------------------------+\n' - '| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n' - '| | Dil Pickles |\n' - '+------------+-------------------------------------------------------------------------------------+\n' - '| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |\n' - '+------------+-------------------------------------------------------------------------------------+' + "+------------+-------------------------------------------------------------------------------------+\n" + "| Show | Characters |\n" + "+------------+-------------------------------------------------------------------------------------+\n" + "| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n" + "| | Dil Pickles |\n" + "+------------+-------------------------------------------------------------------------------------+\n" + "| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |\n" + "+------------+-------------------------------------------------------------------------------------+" ) assert actual == expected # Justify right. - table.justify_columns = {1: 'right'} + table.justify_columns = {1: "right"} actual = table.table expected = ( - '+------------+-------------------------------------------------------------------------------------+\n' - '| Show | Characters |\n' - '+------------+-------------------------------------------------------------------------------------+\n' - '| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n' - '| | Dil Pickles |\n' - '+------------+-------------------------------------------------------------------------------------+\n' - '| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |\n' - '+------------+-------------------------------------------------------------------------------------+' + "+------------+-------------------------------------------------------------------------------------+\n" + "| Show | Characters |\n" + "+------------+-------------------------------------------------------------------------------------+\n" + "| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n" + "| | Dil Pickles |\n" + "+------------+-------------------------------------------------------------------------------------+\n" + "| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |\n" + "+------------+-------------------------------------------------------------------------------------+" ) assert actual == expected @pytest.mark.skipif(str(not IS_WINDOWS)) -@pytest.mark.skip # https://github.com/Robpol86/terminaltables/issues/44 +@pytest.mark.skip # https://github.com/Robpol86/terminaltables3/issues/44 def test_windows_screenshot(tmpdir): """Test on Windows in a new console window. Take a screenshot to verify it works. :param tmpdir: pytest fixture. """ - script = tmpdir.join('script.py') + script = tmpdir.join("script.py") command = [sys.executable, str(script)] - screenshot = PROJECT_ROOT.join('test_ascii_table.png') + screenshot = PROJECT_ROOT.join("test_ascii_table.png") if screenshot.check(): screenshot.remove() # Generate script. - script_template = dedent(u"""\ + script_template = dedent( + """\ from __future__ import print_function import os, time from colorclass import Color, Windows - from terminaltables import AsciiTable + from terminaltables3 import AsciiTable Windows.enable(auto_colors=True) stop_after = time.time() + 20 @@ -132,12 +136,13 @@ def test_windows_screenshot(tmpdir): print('Waiting for screenshot_until_match()...') while not os.path.exists(r'%s') and time.time() < stop_after: time.sleep(0.5) - """) + """ + ) script_contents = script_template % str(screenshot) - script.write(script_contents.encode('utf-8'), mode='wb') + script.write(script_contents.encode("utf-8"), mode="wb") # Setup expected. - sub_images = [str(p) for p in HERE.listdir('sub_ascii_*.bmp')] + sub_images = [str(p) for p in HERE.listdir("sub_ascii_*.bmp")] assert sub_images # Run. diff --git a/tests/test_all_tables_e2e/test_double_table.py b/tests/test_all_tables_e2e/test_double_table.py index beec764..3cd54e6 100644 --- a/tests/test_all_tables_e2e/test_double_table.py +++ b/tests/test_all_tables_e2e/test_double_table.py @@ -6,8 +6,8 @@ from textwrap import dedent import py import pytest -from terminaltables import DoubleTable -from terminaltables.terminal_io import IS_WINDOWS +from terminaltables3 import DoubleTable +from terminaltables3.terminal_io import IS_WINDOWS from tests import PROJECT_ROOT from tests.screenshot import RunNewConsole, screenshot_until_match @@ -17,44 +17,35 @@ HERE = py.path.local(__file__).dirpath() def test_single_line(): """Test single-lined cells.""" table_data = [ - ['Name', 'Color', 'Type'], - ['Avocado', 'green', 'nut'], - ['Tomato', 'red', 'fruit'], - ['Lettuce', 'green', 'vegetable'], - ['Watermelon', 'green'], + ["Name", "Color", "Type"], + ["Avocado", "green", "nut"], + ["Tomato", "red", "fruit"], + ["Lettuce", "green", "vegetable"], + ["Watermelon", "green"], [], ] - table = DoubleTable(table_data, 'Example') + table = DoubleTable(table_data, "Example") table.inner_footing_row_border = True - table.justify_columns[0] = 'left' - table.justify_columns[1] = 'center' - table.justify_columns[2] = 'right' + table.justify_columns[0] = "left" + table.justify_columns[1] = "center" + table.justify_columns[2] = "right" actual = table.table expected = ( - u'\u2554Example\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n' - - u'\u2551 Name \u2551 Color \u2551 Type \u2551\n' - - u'\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n' - - u'\u2551 Avocado \u2551 green \u2551 nut \u2551\n' - - u'\u2551 Tomato \u2551 red \u2551 fruit \u2551\n' - - u'\u2551 Lettuce \u2551 green \u2551 vegetable \u2551\n' - - u'\u2551 Watermelon \u2551 green \u2551 \u2551\n' - - u'\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n' - - u'\u2551 \u2551 \u2551 \u2551\n' - - u'\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d' + "\u2554Example\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n" + "\u2551 Name \u2551 Color \u2551 Type \u2551\n" + "\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n" + "\u2551 Avocado \u2551 green \u2551 nut \u2551\n" + "\u2551 Tomato \u2551 red \u2551 fruit \u2551\n" + "\u2551 Lettuce \u2551 green \u2551 vegetable \u2551\n" + "\u2551 Watermelon \u2551 green \u2551 \u2551\n" + "\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n" + "\u2551 \u2551 \u2551 \u2551\n" + "\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d" ) assert actual == expected @@ -62,47 +53,44 @@ def test_single_line(): def test_multi_line(): """Test multi-lined cells.""" table_data = [ - ['Show', 'Characters'], - ['Rugrats', 'Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles'], - ['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] + ["Show", "Characters"], + [ + "Rugrats", + "Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles", + ], + ["South Park", "Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick"], ] table = DoubleTable(table_data) # Test defaults. actual = table.table expected = ( - u'\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n' - - u'\u2551 Show \u2551 Characters ' - u'\u2551\n' - - u'\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n' - - u'\u2551 Rugrats \u2551 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, ' - u'\u2551\n' - - u'\u2551 \u2551 Dil Pickles ' - u'\u2551\n' - - u'\u2551 South Park \u2551 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' - u'\u2551\n' - - u'\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d' + "\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n" + "\u2551 Show \u2551 Characters " + "\u2551\n" + "\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n" + "\u2551 Rugrats \u2551 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, " + "\u2551\n" + "\u2551 \u2551 Dil Pickles " + "\u2551\n" + "\u2551 South Park \u2551 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick " + "\u2551\n" + "\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d" ) assert actual == expected @@ -110,114 +98,101 @@ def test_multi_line(): table.inner_row_border = True actual = table.table expected = ( - u'\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n' - - u'\u2551 Show \u2551 Characters ' - u'\u2551\n' - - u'\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n' - - u'\u2551 Rugrats \u2551 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, ' - u'\u2551\n' - - u'\u2551 \u2551 Dil Pickles ' - u'\u2551\n' - - u'\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n' - - u'\u2551 South Park \u2551 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' - u'\u2551\n' - - u'\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d' + "\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n" + "\u2551 Show \u2551 Characters " + "\u2551\n" + "\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n" + "\u2551 Rugrats \u2551 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, " + "\u2551\n" + "\u2551 \u2551 Dil Pickles " + "\u2551\n" + "\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n" + "\u2551 South Park \u2551 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick " + "\u2551\n" + "\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d" ) assert actual == expected # Justify right. - table.justify_columns = {1: 'right'} + table.justify_columns = {1: "right"} actual = table.table expected = ( - u'\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n' - - u'\u2551 Show \u2551 Characters ' - u'\u2551\n' - - u'\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n' - - u'\u2551 Rugrats \u2551 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, ' - u'\u2551\n' - - u'\u2551 \u2551 Dil Pickles ' - u'\u2551\n' - - u'\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n' - - u'\u2551 South Park \u2551 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' - u'\u2551\n' - - u'\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' - u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d' + "\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n" + "\u2551 Show \u2551 Characters " + "\u2551\n" + "\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n" + "\u2551 Rugrats \u2551 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, " + "\u2551\n" + "\u2551 \u2551 Dil Pickles " + "\u2551\n" + "\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n" + "\u2551 South Park \u2551 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick " + "\u2551\n" + "\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" + "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d" ) assert actual == expected @pytest.mark.skipif(str(not IS_WINDOWS)) -@pytest.mark.skip # https://github.com/Robpol86/terminaltables/issues/44 +@pytest.mark.skip # https://github.com/Robpol86/terminaltables3/issues/44 def test_windows_screenshot(tmpdir): """Test on Windows in a new console window. Take a screenshot to verify it works. :param tmpdir: pytest fixture. """ - script = tmpdir.join('script.py') + script = tmpdir.join("script.py") command = [sys.executable, str(script)] - screenshot = PROJECT_ROOT.join('test_double_table.png') + screenshot = PROJECT_ROOT.join("test_double_table.png") if screenshot.check(): screenshot.remove() # Generate script. - script_template = dedent(u"""\ + script_template = dedent( + """\ from __future__ import print_function import os, time from colorclass import Color, Windows - from terminaltables import DoubleTable + from terminaltables3 import DoubleTable Windows.enable(auto_colors=True) stop_after = time.time() + 20 @@ -232,12 +207,13 @@ def test_windows_screenshot(tmpdir): print('Waiting for screenshot_until_match()...') while not os.path.exists(r'%s') and time.time() < stop_after: time.sleep(0.5) - """) + """ + ) script_contents = script_template % str(screenshot) - script.write(script_contents.encode('utf-8'), mode='wb') + script.write(script_contents.encode("utf-8"), mode="wb") # Setup expected. - sub_images = [str(p) for p in HERE.listdir('sub_double_*.bmp')] + sub_images = [str(p) for p in HERE.listdir("sub_double_*.bmp")] assert sub_images # Run. diff --git a/tests/test_all_tables_e2e/test_github_table.py b/tests/test_all_tables_e2e/test_github_table.py index 6176215..4764d0e 100644 --- a/tests/test_all_tables_e2e/test_github_table.py +++ b/tests/test_all_tables_e2e/test_github_table.py @@ -1,33 +1,33 @@ """GithubFlavoredMarkdownTable end to end testing.""" -from terminaltables import GithubFlavoredMarkdownTable +from terminaltables3 import GithubFlavoredMarkdownTable def test_single_line(): """Test single-lined cells.""" table_data = [ - ['Name', 'Color', 'Type'], - ['Avocado', 'green', 'nut'], - ['Tomato', 'red', 'fruit'], - ['Lettuce', 'green', 'vegetable'], - ['Watermelon', 'green'], + ["Name", "Color", "Type"], + ["Avocado", "green", "nut"], + ["Tomato", "red", "fruit"], + ["Lettuce", "green", "vegetable"], + ["Watermelon", "green"], [], ] table = GithubFlavoredMarkdownTable(table_data) table.inner_footing_row_border = True - table.justify_columns[0] = 'left' - table.justify_columns[1] = 'center' - table.justify_columns[2] = 'right' + table.justify_columns[0] = "left" + table.justify_columns[1] = "center" + table.justify_columns[2] = "right" actual = table.table expected = ( - '| Name | Color | Type |\n' - '|:-----------|:-----:|----------:|\n' - '| Avocado | green | nut |\n' - '| Tomato | red | fruit |\n' - '| Lettuce | green | vegetable |\n' - '| Watermelon | green | |\n' - '| | | |' + "| Name | Color | Type |\n" + "|:-----------|:-----:|----------:|\n" + "| Avocado | green | nut |\n" + "| Tomato | red | fruit |\n" + "| Lettuce | green | vegetable |\n" + "| Watermelon | green | |\n" + "| | | |" ) assert actual == expected @@ -35,20 +35,23 @@ def test_single_line(): def test_multi_line(): """Test multi-lined cells.""" table_data = [ - ['Show', 'Characters'], - ['Rugrats', 'Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles'], - ['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] + ["Show", "Characters"], + [ + "Rugrats", + "Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles", + ], + ["South Park", "Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick"], ] table = GithubFlavoredMarkdownTable(table_data) # Test defaults. actual = table.table expected = ( - '| Show | Characters |\n' - '|------------|-------------------------------------------------------------------------------------|\n' - '| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n' - '| | Dil Pickles |\n' - '| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |' + "| Show | Characters |\n" + "|------------|-------------------------------------------------------------------------------------|\n" + "| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n" + "| | Dil Pickles |\n" + "| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |" ) assert actual == expected @@ -56,22 +59,22 @@ def test_multi_line(): table.inner_row_border = True actual = table.table expected = ( - '| Show | Characters |\n' - '|------------|-------------------------------------------------------------------------------------|\n' - '| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n' - '| | Dil Pickles |\n' - '| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |' + "| Show | Characters |\n" + "|------------|-------------------------------------------------------------------------------------|\n" + "| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n" + "| | Dil Pickles |\n" + "| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |" ) assert actual == expected # Justify right. - table.justify_columns = {1: 'right'} + table.justify_columns = {1: "right"} actual = table.table expected = ( - '| Show | Characters |\n' - '|------------|------------------------------------------------------------------------------------:|\n' - '| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n' - '| | Dil Pickles |\n' - '| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |' + "| Show | Characters |\n" + "|------------|------------------------------------------------------------------------------------:|\n" + "| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n" + "| | Dil Pickles |\n" + "| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |" ) assert actual == expected diff --git a/tests/test_all_tables_e2e/test_porcelain_table.py b/tests/test_all_tables_e2e/test_porcelain_table.py index 7677188..ced2f9c 100644 --- a/tests/test_all_tables_e2e/test_porcelain_table.py +++ b/tests/test_all_tables_e2e/test_porcelain_table.py @@ -1,29 +1,29 @@ """PorcelainTable end to end testing.""" -from terminaltables import PorcelainTable +from terminaltables3 import PorcelainTable def test_single_line(): """Test single-lined cells.""" table_data = [ - ['Name', 'Color', 'Type'], - ['Avocado', 'green', 'nut'], - ['Tomato', 'red', 'fruit'], - ['Lettuce', 'green', 'vegetable'], - ['Watermelon', 'green'] + ["Name", "Color", "Type"], + ["Avocado", "green", "nut"], + ["Tomato", "red", "fruit"], + ["Lettuce", "green", "vegetable"], + ["Watermelon", "green"], ] table = PorcelainTable(table_data) - table.justify_columns[0] = 'left' - table.justify_columns[1] = 'center' - table.justify_columns[2] = 'right' + table.justify_columns[0] = "left" + table.justify_columns[1] = "center" + table.justify_columns[2] = "right" actual = table.table expected = ( - ' Name | Color | Type \n' - ' Avocado | green | nut \n' - ' Tomato | red | fruit \n' - ' Lettuce | green | vegetable \n' - ' Watermelon | green | ' + " Name | Color | Type \n" + " Avocado | green | nut \n" + " Tomato | red | fruit \n" + " Lettuce | green | vegetable \n" + " Watermelon | green | " ) assert actual == expected @@ -31,29 +31,32 @@ def test_single_line(): def test_multi_line(): """Test multi-lined cells.""" table_data = [ - ['Show', 'Characters'], - ['Rugrats', 'Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles'], - ['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] + ["Show", "Characters"], + [ + "Rugrats", + "Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles", + ], + ["South Park", "Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick"], ] table = PorcelainTable(table_data) # Test defaults. actual = table.table expected = ( - ' Show | Characters \n' - ' Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, \n' - ' | Dil Pickles \n' - ' South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' + " Show | Characters \n" + " Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, \n" + " | Dil Pickles \n" + " South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick " ) assert actual == expected # Justify right. - table.justify_columns = {1: 'right'} + table.justify_columns = {1: "right"} actual = table.table expected = ( - ' Show | Characters \n' - ' Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, \n' - ' | Dil Pickles \n' - ' South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' + " Show | Characters \n" + " Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, \n" + " | Dil Pickles \n" + " South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick " ) assert actual == expected diff --git a/tests/test_all_tables_e2e/test_single_table.py b/tests/test_all_tables_e2e/test_single_table.py index f4fa6b9..7c6b8ff 100644 --- a/tests/test_all_tables_e2e/test_single_table.py +++ b/tests/test_all_tables_e2e/test_single_table.py @@ -2,8 +2,8 @@ import pytest -from terminaltables import SingleTable -from terminaltables.terminal_io import IS_WINDOWS +from terminaltables3 import SingleTable +from terminaltables3.terminal_io import IS_WINDOWS pytestmark = pytest.mark.skipif(str(IS_WINDOWS)) @@ -11,44 +11,35 @@ pytestmark = pytest.mark.skipif(str(IS_WINDOWS)) def test_single_line(): """Test single-lined cells.""" table_data = [ - ['Name', 'Color', 'Type'], - ['Avocado', 'green', 'nut'], - ['Tomato', 'red', 'fruit'], - ['Lettuce', 'green', 'vegetable'], - ['Watermelon', 'green'], + ["Name", "Color", "Type"], + ["Avocado", "green", "nut"], + ["Tomato", "red", "fruit"], + ["Lettuce", "green", "vegetable"], + ["Watermelon", "green"], [], ] - table = SingleTable(table_data, 'Example') + table = SingleTable(table_data, "Example") table.inner_footing_row_border = True - table.justify_columns[0] = 'left' - table.justify_columns[1] = 'center' - table.justify_columns[2] = 'right' + table.justify_columns[0] = "left" + table.justify_columns[1] = "center" + table.justify_columns[2] = "right" actual = table.table expected = ( - '\033(0\x6c\033(BExample\033(0\x71\x71\x71\x71\x71\x77\x71\x71\x71\x71\x71\x71\x71\x77\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x6b\033(B\n' - - '\033(0\x78\033(B Name \033(0\x78\033(B Color \033(0\x78\033(B Type \033(0\x78\033(B\n' - - '\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n' - - '\033(0\x78\033(B Avocado \033(0\x78\033(B green \033(0\x78\033(B nut \033(0\x78\033(B\n' - - '\033(0\x78\033(B Tomato \033(0\x78\033(B red \033(0\x78\033(B fruit \033(0\x78\033(B\n' - - '\033(0\x78\033(B Lettuce \033(0\x78\033(B green \033(0\x78\033(B vegetable \033(0\x78\033(B\n' - - '\033(0\x78\033(B Watermelon \033(0\x78\033(B green \033(0\x78\033(B \033(0\x78\033(B\n' - - '\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n' - - '\033(0\x78\033(B \033(0\x78\033(B \033(0\x78\033(B \033(0\x78\033(B\n' - - '\033(0\x6d\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x76\x71\x71\x71\x71\x71\x71\x71\x76\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x6a\033(B' + "\033(0\x6c\033(BExample\033(0\x71\x71\x71\x71\x71\x77\x71\x71\x71\x71\x71\x71\x71\x77\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x6b\033(B\n" + "\033(0\x78\033(B Name \033(0\x78\033(B Color \033(0\x78\033(B Type \033(0\x78\033(B\n" + "\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n" + "\033(0\x78\033(B Avocado \033(0\x78\033(B green \033(0\x78\033(B nut \033(0\x78\033(B\n" + "\033(0\x78\033(B Tomato \033(0\x78\033(B red \033(0\x78\033(B fruit \033(0\x78\033(B\n" + "\033(0\x78\033(B Lettuce \033(0\x78\033(B green \033(0\x78\033(B vegetable \033(0\x78\033(B\n" + "\033(0\x78\033(B Watermelon \033(0\x78\033(B green \033(0\x78\033(B \033(0\x78\033(B\n" + "\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n" + "\033(0\x78\033(B \033(0\x78\033(B \033(0\x78\033(B \033(0\x78\033(B\n" + "\033(0\x6d\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x76\x71\x71\x71\x71\x71\x71\x71\x76\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x6a\033(B" ) assert actual == expected @@ -56,41 +47,38 @@ def test_single_line(): def test_multi_line(): """Test multi-lined cells.""" table_data = [ - ['Show', 'Characters'], - ['Rugrats', 'Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles'], - ['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] + ["Show", "Characters"], + [ + "Rugrats", + "Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles", + ], + ["South Park", "Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick"], ] table = SingleTable(table_data) # Test defaults. actual = table.table expected = ( - '\033(0\x6c\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x77\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6b\033(B\n' - - '\033(0\x78\033(B Show \033(0\x78\033(B Characters ' - ' \033(0\x78\033(B\n' - - '\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n' - - '\033(0\x78\033(B Rugrats \033(0\x78\033(B Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille,' - ' Angelica Pickles, \033(0\x78\033(B\n' - - '\033(0\x78\033(B \033(0\x78\033(B Dil Pickles ' - ' \033(0\x78\033(B\n' - - '\033(0\x78\033(B South Park \033(0\x78\033(B Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' - ' \033(0\x78\033(B\n' - - '\033(0\x6d\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x76\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6a\033(B' + "\033(0\x6c\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x77\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6b\033(B\n" + "\033(0\x78\033(B Show \033(0\x78\033(B Characters " + " \033(0\x78\033(B\n" + "\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n" + "\033(0\x78\033(B Rugrats \033(0\x78\033(B Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille," + " Angelica Pickles, \033(0\x78\033(B\n" + "\033(0\x78\033(B \033(0\x78\033(B Dil Pickles " + " \033(0\x78\033(B\n" + "\033(0\x78\033(B South Park \033(0\x78\033(B Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick " + " \033(0\x78\033(B\n" + "\033(0\x6d\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x76\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6a\033(B" ) assert actual == expected @@ -98,74 +86,60 @@ def test_multi_line(): table.inner_row_border = True actual = table.table expected = ( - '\033(0\x6c\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x77\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6b\033(B\n' - - '\033(0\x78\033(B Show \033(0\x78\033(B Characters ' - ' \033(0\x78\033(B\n' - - '\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n' - - '\033(0\x78\033(B Rugrats \033(0\x78\033(B Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille,' - ' Angelica Pickles, \033(0\x78\033(B\n' - - '\033(0\x78\033(B \033(0\x78\033(B Dil Pickles ' - ' \033(0\x78\033(B\n' - - '\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n' - - '\033(0\x78\033(B South Park \033(0\x78\033(B Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' - ' \033(0\x78\033(B\n' - - '\033(0\x6d\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x76\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6a\033(B' + "\033(0\x6c\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x77\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6b\033(B\n" + "\033(0\x78\033(B Show \033(0\x78\033(B Characters " + " \033(0\x78\033(B\n" + "\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n" + "\033(0\x78\033(B Rugrats \033(0\x78\033(B Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille," + " Angelica Pickles, \033(0\x78\033(B\n" + "\033(0\x78\033(B \033(0\x78\033(B Dil Pickles " + " \033(0\x78\033(B\n" + "\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n" + "\033(0\x78\033(B South Park \033(0\x78\033(B Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick " + " \033(0\x78\033(B\n" + "\033(0\x6d\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x76\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6a\033(B" ) assert actual == expected # Justify right. - table.justify_columns = {1: 'right'} + table.justify_columns = {1: "right"} actual = table.table expected = ( - '\033(0\x6c\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x77\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6b\033(B\n' - - '\033(0\x78\033(B Show \033(0\x78\033(B ' - ' Characters \033(0\x78\033(B\n' - - '\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n' - - '\033(0\x78\033(B Rugrats \033(0\x78\033(B Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille,' - ' Angelica Pickles, \033(0\x78\033(B\n' - - '\033(0\x78\033(B \033(0\x78\033(B ' - ' Dil Pickles \033(0\x78\033(B\n' - - '\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n' - - '\033(0\x78\033(B South Park \033(0\x78\033(B Stan Marsh, Kyle Broflovski, ' - 'Eric Cartman, Kenny McCormick \033(0\x78\033(B\n' - - '\033(0\x6d\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x76\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' - '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6a\033(B' + "\033(0\x6c\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x77\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6b\033(B\n" + "\033(0\x78\033(B Show \033(0\x78\033(B " + " Characters \033(0\x78\033(B\n" + "\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n" + "\033(0\x78\033(B Rugrats \033(0\x78\033(B Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille," + " Angelica Pickles, \033(0\x78\033(B\n" + "\033(0\x78\033(B \033(0\x78\033(B " + " Dil Pickles \033(0\x78\033(B\n" + "\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n" + "\033(0\x78\033(B South Park \033(0\x78\033(B Stan Marsh, Kyle Broflovski, " + "Eric Cartman, Kenny McCormick \033(0\x78\033(B\n" + "\033(0\x6d\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x76\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71" + "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6a\033(B" ) assert actual == expected diff --git a/tests/test_all_tables_e2e/test_single_table_windows.py b/tests/test_all_tables_e2e/test_single_table_windows.py index bfdfe2e..120cf84 100644 --- a/tests/test_all_tables_e2e/test_single_table_windows.py +++ b/tests/test_all_tables_e2e/test_single_table_windows.py @@ -6,8 +6,8 @@ from textwrap import dedent import py import pytest -from terminaltables import SingleTable -from terminaltables.terminal_io import IS_WINDOWS +from terminaltables3 import SingleTable +from terminaltables3.terminal_io import IS_WINDOWS from tests import PROJECT_ROOT from tests.screenshot import RunNewConsole, screenshot_until_match @@ -18,44 +18,35 @@ pytestmark = pytest.mark.skipif(str(not IS_WINDOWS)) def test_single_line(): """Test single-lined cells.""" table_data = [ - ['Name', 'Color', 'Type'], - ['Avocado', 'green', 'nut'], - ['Tomato', 'red', 'fruit'], - ['Lettuce', 'green', 'vegetable'], - ['Watermelon', 'green'], + ["Name", "Color", "Type"], + ["Avocado", "green", "nut"], + ["Tomato", "red", "fruit"], + ["Lettuce", "green", "vegetable"], + ["Watermelon", "green"], [], ] - table = SingleTable(table_data, 'Example') + table = SingleTable(table_data, "Example") table.inner_footing_row_border = True - table.justify_columns[0] = 'left' - table.justify_columns[1] = 'center' - table.justify_columns[2] = 'right' + table.justify_columns[0] = "left" + table.justify_columns[1] = "center" + table.justify_columns[2] = "right" actual = table.table expected = ( - u'\u250cExample\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n' - - u'\u2502 Name \u2502 Color \u2502 Type \u2502\n' - - u'\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n' - - u'\u2502 Avocado \u2502 green \u2502 nut \u2502\n' - - u'\u2502 Tomato \u2502 red \u2502 fruit \u2502\n' - - u'\u2502 Lettuce \u2502 green \u2502 vegetable \u2502\n' - - u'\u2502 Watermelon \u2502 green \u2502 \u2502\n' - - u'\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n' - - u'\u2502 \u2502 \u2502 \u2502\n' - - u'\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518' + "\u250cExample\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n" + "\u2502 Name \u2502 Color \u2502 Type \u2502\n" + "\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n" + "\u2502 Avocado \u2502 green \u2502 nut \u2502\n" + "\u2502 Tomato \u2502 red \u2502 fruit \u2502\n" + "\u2502 Lettuce \u2502 green \u2502 vegetable \u2502\n" + "\u2502 Watermelon \u2502 green \u2502 \u2502\n" + "\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n" + "\u2502 \u2502 \u2502 \u2502\n" + "\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518" ) assert actual == expected @@ -63,47 +54,44 @@ def test_single_line(): def test_multi_line(): """Test multi-lined cells.""" table_data = [ - ['Show', 'Characters'], - ['Rugrats', 'Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles'], - ['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] + ["Show", "Characters"], + [ + "Rugrats", + "Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles", + ], + ["South Park", "Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick"], ] table = SingleTable(table_data) # Test defaults. actual = table.table expected = ( - u'\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n' - - u'\u2502 Show \u2502 Characters ' - u'\u2502\n' - - u'\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n' - - u'\u2502 Rugrats \u2502 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, ' - u'\u2502\n' - - u'\u2502 \u2502 Dil Pickles ' - u'\u2502\n' - - u'\u2502 South Park \u2502 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' - u'\u2502\n' - - u'\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518' + "\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n" + "\u2502 Show \u2502 Characters " + "\u2502\n" + "\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n" + "\u2502 Rugrats \u2502 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, " + "\u2502\n" + "\u2502 \u2502 Dil Pickles " + "\u2502\n" + "\u2502 South Park \u2502 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick " + "\u2502\n" + "\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518" ) assert actual == expected @@ -111,114 +99,101 @@ def test_multi_line(): table.inner_row_border = True actual = table.table expected = ( - u'\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n' - - u'\u2502 Show \u2502 Characters ' - u'\u2502\n' - - u'\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n' - - u'\u2502 Rugrats \u2502 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, ' - u'\u2502\n' - - u'\u2502 \u2502 Dil Pickles ' - u'\u2502\n' - - u'\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n' - - u'\u2502 South Park \u2502 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' - u'\u2502\n' - - u'\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518' + "\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n" + "\u2502 Show \u2502 Characters " + "\u2502\n" + "\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n" + "\u2502 Rugrats \u2502 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, " + "\u2502\n" + "\u2502 \u2502 Dil Pickles " + "\u2502\n" + "\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n" + "\u2502 South Park \u2502 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick " + "\u2502\n" + "\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518" ) assert actual == expected # Justify right. - table.justify_columns = {1: 'right'} + table.justify_columns = {1: "right"} actual = table.table expected = ( - u'\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n' - - u'\u2502 Show \u2502 Characters ' - u'\u2502\n' - - u'\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n' - - u'\u2502 Rugrats \u2502 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, ' - u'\u2502\n' - - u'\u2502 \u2502 Dil Pickles ' - u'\u2502\n' - - u'\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n' - - u'\u2502 South Park \u2502 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' - u'\u2502\n' - - u'\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' - u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518' + "\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n" + "\u2502 Show \u2502 Characters " + "\u2502\n" + "\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n" + "\u2502 Rugrats \u2502 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, " + "\u2502\n" + "\u2502 \u2502 Dil Pickles " + "\u2502\n" + "\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n" + "\u2502 South Park \u2502 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick " + "\u2502\n" + "\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" + "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518" ) assert actual == expected @pytest.mark.skipif(str(not IS_WINDOWS)) -@pytest.mark.skip # https://github.com/Robpol86/terminaltables/issues/44 +@pytest.mark.skip # https://github.com/Robpol86/terminaltables3/issues/44 def test_windows_screenshot(tmpdir): """Test on Windows in a new console window. Take a screenshot to verify it works. :param tmpdir: pytest fixture. """ - script = tmpdir.join('script.py') + script = tmpdir.join("script.py") command = [sys.executable, str(script)] - screenshot = PROJECT_ROOT.join('test_single_table.png') + screenshot = PROJECT_ROOT.join("test_single_table.png") if screenshot.check(): screenshot.remove() # Generate script. - script_template = dedent(u"""\ + script_template = dedent( + """\ from __future__ import print_function import os, time from colorclass import Color, Windows - from terminaltables import SingleTable + from terminaltables3 import SingleTable Windows.enable(auto_colors=True) stop_after = time.time() + 20 @@ -233,12 +208,13 @@ def test_windows_screenshot(tmpdir): print('Waiting for screenshot_until_match()...') while not os.path.exists(r'%s') and time.time() < stop_after: time.sleep(0.5) - """) + """ + ) script_contents = script_template % str(screenshot) - script.write(script_contents.encode('utf-8'), mode='wb') + script.write(script_contents.encode("utf-8"), mode="wb") # Setup expected. - sub_images = [str(p) for p in HERE.listdir('sub_single_*.bmp')] + sub_images = [str(p) for p in HERE.listdir("sub_single_*.bmp")] assert sub_images # Run. diff --git a/tests/test_ascii_table.py b/tests/test_ascii_table.py index 020a443..dd3dc8e 100644 --- a/tests/test_ascii_table.py +++ b/tests/test_ascii_table.py @@ -2,19 +2,22 @@ import pytest -from terminaltables.other_tables import AsciiTable +from terminaltables3.other_tables import AsciiTable SINGLE_LINE = ( - ('Name', 'Color', 'Type'), - ('Avocado', 'green', 'nut'), - ('Tomato', 'red', 'fruit'), - ('Lettuce', 'green', 'vegetable'), + ("Name", "Color", "Type"), + ("Avocado", "green", "nut"), + ("Tomato", "red", "fruit"), + ("Lettuce", "green", "vegetable"), ) MULTI_LINE = ( - ('Show', 'Characters'), - ('Rugrats', 'Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles'), - ('South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'), + ("Show", "Characters"), + ( + "Rugrats", + "Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles", + ), + ("South Park", "Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick"), ) @@ -24,20 +27,25 @@ def patch(monkeypatch): :param monkeypatch: pytest fixture. """ - monkeypatch.setattr('terminaltables.ascii_table.terminal_size', lambda: (79, 24)) - monkeypatch.setattr('terminaltables.width_and_alignment.terminal_size', lambda: (79, 24)) - - -@pytest.mark.parametrize('table_data,column_number,expected', [ - ([], 0, IndexError), - ([[]], 0, IndexError), - ([['']], 1, IndexError), - (SINGLE_LINE, 0, 55), - (SINGLE_LINE, 1, 53), - (SINGLE_LINE, 2, 57), - (MULTI_LINE, 0, -11), - (MULTI_LINE, 1, 62), -]) + monkeypatch.setattr("terminaltables3.ascii_table.terminal_size", lambda: (79, 24)) + monkeypatch.setattr( + "terminaltables3.width_and_alignment.terminal_size", lambda: (79, 24) + ) + + +@pytest.mark.parametrize( + "table_data,column_number,expected", + [ + ([], 0, IndexError), + ([[]], 0, IndexError), + ([[""]], 1, IndexError), + (SINGLE_LINE, 0, 55), + (SINGLE_LINE, 1, 53), + (SINGLE_LINE, 2, 57), + (MULTI_LINE, 0, -11), + (MULTI_LINE, 1, 62), + ], +) def test_column_max_width(table_data, column_number, expected): """Test method in class. @@ -47,7 +55,7 @@ def test_column_max_width(table_data, column_number, expected): """ table = AsciiTable(table_data) - if expected == IndexError: + if expected is IndexError: with pytest.raises(IndexError): table.column_max_width(column_number) return @@ -58,22 +66,25 @@ def test_column_max_width(table_data, column_number, expected): def test_column_widths(): """Test method in class.""" - assert AsciiTable([]).column_widths == list() + assert AsciiTable([]).column_widths == [] table = AsciiTable(SINGLE_LINE) actual = table.column_widths assert actual == [7, 5, 9] -@pytest.mark.parametrize('table_data,terminal_width,expected', [ - ([], None, True), - ([[]], None, True), - ([['']], None, True), - (SINGLE_LINE, None, True), - (SINGLE_LINE, 30, False), - (MULTI_LINE, None, False), - (MULTI_LINE, 100, True), -]) +@pytest.mark.parametrize( + "table_data,terminal_width,expected", + [ + ([], None, True), + ([[]], None, True), + ([[""]], None, True), + (SINGLE_LINE, None, True), + (SINGLE_LINE, 30, False), + (MULTI_LINE, None, False), + (MULTI_LINE, 100, True), + ], +) def test_ok(monkeypatch, table_data, terminal_width, expected): """Test method in class. @@ -83,20 +94,25 @@ def test_ok(monkeypatch, table_data, terminal_width, expected): :param bool expected: Expected return value. """ if terminal_width is not None: - monkeypatch.setattr('terminaltables.ascii_table.terminal_size', lambda: (terminal_width, 24)) + monkeypatch.setattr( + "terminaltables3.ascii_table.terminal_size", lambda: (terminal_width, 24) + ) table = AsciiTable(table_data) actual = table.ok assert actual is expected -@pytest.mark.parametrize('table_data,expected', [ - ([], 2), - ([[]], 2), - ([['']], 4), - ([[' ']], 5), - (SINGLE_LINE, 31), - (MULTI_LINE, 100), -]) +@pytest.mark.parametrize( + "table_data,expected", + [ + ([], 2), + ([[]], 2), + ([[""]], 4), + ([[" "]], 5), + (SINGLE_LINE, 31), + (MULTI_LINE, 100), + ], +) def test_table_width(table_data, expected): """Test method in class. diff --git a/tests/test_base_table/__init__.py b/tests/test_base_table/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/test_base_table/__init__.py diff --git a/tests/test_base_table/test_gen_row_lines.py b/tests/test_base_table/test_gen_row_lines.py index 0d0f43c..da47349 100644 --- a/tests/test_base_table/test_gen_row_lines.py +++ b/tests/test_base_table/test_gen_row_lines.py @@ -2,47 +2,47 @@ import pytest -from terminaltables.base_table import BaseTable +from terminaltables3.base_table import BaseTable -@pytest.mark.parametrize('style', ['heading', 'footing', 'row']) +@pytest.mark.parametrize("style", ["heading", "footing", "row"]) def test_single_line(style): """Test with single-line row. :param str style: Passed to method. """ - row = ['Row One Column One', 'Two', 'Three'] + row = ["Row One Column One", "Two", "Three"] table = BaseTable([row]) actual = [tuple(i) for i in table.gen_row_lines(row, style, [18, 3, 5], 1)] expected = [ - ('|', ' Row One Column One ', '|', ' Two ', '|', ' Three ', '|'), + ("|", " Row One Column One ", "|", " Two ", "|", " Three ", "|"), ] assert actual == expected -@pytest.mark.parametrize('style', ['heading', 'footing', 'row']) +@pytest.mark.parametrize("style", ["heading", "footing", "row"]) def test_multi_line(style): """Test with multi-line row. :param str style: Passed to method. """ - row = ['Row One\nColumn One', 'Two', 'Three'] + row = ["Row One\nColumn One", "Two", "Three"] table = BaseTable([row]) actual = [tuple(i) for i in table.gen_row_lines(row, style, [10, 3, 5], 2)] expected = [ - ('|', ' Row One ', '|', ' Two ', '|', ' Three ', '|'), - ('|', ' Column One ', '|', ' ', '|', ' ', '|'), + ("|", " Row One ", "|", " Two ", "|", " Three ", "|"), + ("|", " Column One ", "|", " ", "|", " ", "|"), ] assert actual == expected -@pytest.mark.parametrize('style', ['heading', 'footing', 'row']) +@pytest.mark.parametrize("style", ["heading", "footing", "row"]) def test_no_padding_no_borders(style): """Test without padding or borders. :param str style: Passed to method. """ - row = ['Row One\nColumn One', 'Two', 'Three'] + row = ["Row One\nColumn One", "Two", "Three"] table = BaseTable([row]) table.inner_column_border = False table.outer_border = False @@ -50,28 +50,28 @@ def test_no_padding_no_borders(style): table.padding_right = 0 actual = [tuple(i) for i in table.gen_row_lines(row, style, [10, 3, 5], 2)] expected = [ - ('Row One ', 'Two', 'Three'), - ('Column One', ' ', ' '), + ("Row One ", "Two", "Three"), + ("Column One", " ", " "), ] assert actual == expected -@pytest.mark.parametrize('style', ['heading', 'footing', 'row']) +@pytest.mark.parametrize("style", ["heading", "footing", "row"]) def test_uneven(style): """Test with row missing cells. :param str style: Passed to method. """ - row = ['Row One Column One'] + row = ["Row One Column One"] table = BaseTable([row]) actual = [tuple(i) for i in table.gen_row_lines(row, style, [18, 3, 5], 1)] expected = [ - ('|', ' Row One Column One ', '|', ' ', '|', ' ', '|'), + ("|", " Row One Column One ", "|", " ", "|", " ", "|"), ] assert actual == expected -@pytest.mark.parametrize('style', ['heading', 'footing', 'row']) +@pytest.mark.parametrize("style", ["heading", "footing", "row"]) def test_empty_table(style): """Test empty table. @@ -81,6 +81,6 @@ def test_empty_table(style): table = BaseTable([row]) actual = [tuple(i) for i in table.gen_row_lines(row, style, [], 0)] expected = [ - ('|', '|'), + ("|", "|"), ] assert actual == expected diff --git a/tests/test_base_table/test_gen_table.py b/tests/test_base_table/test_gen_table.py index 54d5fe1..d7ee997 100644 --- a/tests/test_base_table/test_gen_table.py +++ b/tests/test_base_table/test_gen_table.py @@ -2,15 +2,17 @@ import pytest -from terminaltables.base_table import BaseTable -from terminaltables.build import flatten -from terminaltables.width_and_alignment import max_dimensions +from terminaltables3.base_table import BaseTable +from terminaltables3.build import flatten +from terminaltables3.width_and_alignment import max_dimensions -@pytest.mark.parametrize('inner_heading_row_border', [True, False]) -@pytest.mark.parametrize('inner_footing_row_border', [True, False]) -@pytest.mark.parametrize('inner_row_border', [True, False]) -def test_inner_row_borders(inner_heading_row_border, inner_footing_row_border, inner_row_border): +@pytest.mark.parametrize("inner_heading_row_border", [True, False]) +@pytest.mark.parametrize("inner_footing_row_border", [True, False]) +@pytest.mark.parametrize("inner_row_border", [True, False]) +def test_inner_row_borders( + inner_heading_row_border, inner_footing_row_border, inner_row_border +): """Test heading/footing/row borders. :param bool inner_heading_row_border: Passed to table. @@ -18,142 +20,145 @@ def test_inner_row_borders(inner_heading_row_border, inner_footing_row_border, i :param bool inner_row_border: Passed to table. """ table_data = [ - ['Name', 'Color', 'Type'], - ['Avocado', 'green', 'nut'], - ['Tomato', 'red', 'fruit'], - ['Lettuce', 'green', 'vegetable'], + ["Name", "Color", "Type"], + ["Avocado", "green", "nut"], + ["Tomato", "red", "fruit"], + ["Lettuce", "green", "vegetable"], ] table = BaseTable(table_data) table.inner_heading_row_border = inner_heading_row_border table.inner_footing_row_border = inner_footing_row_border table.inner_row_border = inner_row_border - inner_widths, inner_heights, outer_widths = max_dimensions(table_data, table.padding_left, table.padding_right)[:3] + inner_widths, inner_heights, outer_widths = max_dimensions( + table_data, table.padding_left, table.padding_right + )[:3] actual = flatten(table.gen_table(inner_widths, inner_heights, outer_widths)) # Determine expected. if inner_row_border: expected = ( - '+---------+-------+-----------+\n' - '| Name | Color | Type |\n' - '+---------+-------+-----------+\n' - '| Avocado | green | nut |\n' - '+---------+-------+-----------+\n' - '| Tomato | red | fruit |\n' - '+---------+-------+-----------+\n' - '| Lettuce | green | vegetable |\n' - '+---------+-------+-----------+' + "+---------+-------+-----------+\n" + "| Name | Color | Type |\n" + "+---------+-------+-----------+\n" + "| Avocado | green | nut |\n" + "+---------+-------+-----------+\n" + "| Tomato | red | fruit |\n" + "+---------+-------+-----------+\n" + "| Lettuce | green | vegetable |\n" + "+---------+-------+-----------+" ) elif inner_heading_row_border and inner_footing_row_border: expected = ( - '+---------+-------+-----------+\n' - '| Name | Color | Type |\n' - '+---------+-------+-----------+\n' - '| Avocado | green | nut |\n' - '| Tomato | red | fruit |\n' - '+---------+-------+-----------+\n' - '| Lettuce | green | vegetable |\n' - '+---------+-------+-----------+' + "+---------+-------+-----------+\n" + "| Name | Color | Type |\n" + "+---------+-------+-----------+\n" + "| Avocado | green | nut |\n" + "| Tomato | red | fruit |\n" + "+---------+-------+-----------+\n" + "| Lettuce | green | vegetable |\n" + "+---------+-------+-----------+" ) elif inner_heading_row_border: expected = ( - '+---------+-------+-----------+\n' - '| Name | Color | Type |\n' - '+---------+-------+-----------+\n' - '| Avocado | green | nut |\n' - '| Tomato | red | fruit |\n' - '| Lettuce | green | vegetable |\n' - '+---------+-------+-----------+' + "+---------+-------+-----------+\n" + "| Name | Color | Type |\n" + "+---------+-------+-----------+\n" + "| Avocado | green | nut |\n" + "| Tomato | red | fruit |\n" + "| Lettuce | green | vegetable |\n" + "+---------+-------+-----------+" ) elif inner_footing_row_border: expected = ( - '+---------+-------+-----------+\n' - '| Name | Color | Type |\n' - '| Avocado | green | nut |\n' - '| Tomato | red | fruit |\n' - '+---------+-------+-----------+\n' - '| Lettuce | green | vegetable |\n' - '+---------+-------+-----------+' + "+---------+-------+-----------+\n" + "| Name | Color | Type |\n" + "| Avocado | green | nut |\n" + "| Tomato | red | fruit |\n" + "+---------+-------+-----------+\n" + "| Lettuce | green | vegetable |\n" + "+---------+-------+-----------+" ) else: expected = ( - '+---------+-------+-----------+\n' - '| Name | Color | Type |\n' - '| Avocado | green | nut |\n' - '| Tomato | red | fruit |\n' - '| Lettuce | green | vegetable |\n' - '+---------+-------+-----------+' + "+---------+-------+-----------+\n" + "| Name | Color | Type |\n" + "| Avocado | green | nut |\n" + "| Tomato | red | fruit |\n" + "| Lettuce | green | vegetable |\n" + "+---------+-------+-----------+" ) assert actual == expected -@pytest.mark.parametrize('outer_border', [True, False]) +@pytest.mark.parametrize("outer_border", [True, False]) def test_outer_borders(outer_border): """Test left/right/top/bottom table borders. :param bool outer_border: Passed to table. """ table_data = [ - ['Name', 'Color', 'Type'], - ['Avocado', 'green', 'nut'], - ['Tomato', 'red', 'fruit'], - ['Lettuce', 'green', 'vegetable'], + ["Name", "Color", "Type"], + ["Avocado", "green", "nut"], + ["Tomato", "red", "fruit"], + ["Lettuce", "green", "vegetable"], ] - table = BaseTable(table_data, 'Example Table') + table = BaseTable(table_data, "Example Table") table.outer_border = outer_border - inner_widths, inner_heights, outer_widths = max_dimensions(table_data, table.padding_left, table.padding_right)[:3] + inner_widths, inner_heights, outer_widths = max_dimensions( + table_data, table.padding_left, table.padding_right + )[:3] actual = flatten(table.gen_table(inner_widths, inner_heights, outer_widths)) # Determine expected. if outer_border: expected = ( - '+Example Table----+-----------+\n' - '| Name | Color | Type |\n' - '+---------+-------+-----------+\n' - '| Avocado | green | nut |\n' - '| Tomato | red | fruit |\n' - '| Lettuce | green | vegetable |\n' - '+---------+-------+-----------+' + "+Example Table----+-----------+\n" + "| Name | Color | Type |\n" + "+---------+-------+-----------+\n" + "| Avocado | green | nut |\n" + "| Tomato | red | fruit |\n" + "| Lettuce | green | vegetable |\n" + "+---------+-------+-----------+" ) else: expected = ( - ' Name | Color | Type \n' - '---------+-------+-----------\n' - ' Avocado | green | nut \n' - ' Tomato | red | fruit \n' - ' Lettuce | green | vegetable ' + " Name | Color | Type \n" + "---------+-------+-----------\n" + " Avocado | green | nut \n" + " Tomato | red | fruit \n" + " Lettuce | green | vegetable " ) assert actual == expected -@pytest.mark.parametrize('mode', ['row', 'one', 'blank', 'empty', 'none']) -@pytest.mark.parametrize('bare', [False, True]) +@pytest.mark.parametrize("mode", ["row", "one", "blank", "empty", "none"]) +@pytest.mark.parametrize("bare", [False, True]) def test_one_no_rows(mode, bare): """Test with one or no rows. :param str mode: Type of table contents to test. :param bool bare: Disable padding/borders. """ - if mode == 'row': + if mode == "row": table_data = [ - ['Avocado', 'green', 'nut'], + ["Avocado", "green", "nut"], ] - elif mode == 'one': + elif mode == "one": table_data = [ - ['Avocado'], + ["Avocado"], ] - elif mode == 'blank': + elif mode == "blank": table_data = [ - [''], + [""], ] - elif mode == 'empty': + elif mode == "empty": table_data = [ [], ] else: - table_data = [ - ] + table_data = [] table = BaseTable(table_data) if bare: table.inner_column_border = False @@ -163,63 +168,40 @@ def test_one_no_rows(mode, bare): table.outer_border = False table.padding_left = 0 table.padding_right = 0 - inner_widths, inner_heights, outer_widths = max_dimensions(table_data, table.padding_left, table.padding_right)[:3] + inner_widths, inner_heights, outer_widths = max_dimensions( + table_data, table.padding_left, table.padding_right + )[:3] actual = flatten(table.gen_table(inner_widths, inner_heights, outer_widths)) # Determine expected. - if mode == 'row': + if mode == "row": if bare: - expected = ( - 'Avocadogreennut' - ) + expected = "Avocadogreennut" else: expected = ( - '+---------+-------+-----+\n' - '| Avocado | green | nut |\n' - '+---------+-------+-----+' + "+---------+-------+-----+\n" + "| Avocado | green | nut |\n" + "+---------+-------+-----+" ) - elif mode == 'one': + elif mode == "one": if bare: - expected = ( - 'Avocado' - ) + expected = "Avocado" else: - expected = ( - '+---------+\n' - '| Avocado |\n' - '+---------+' - ) - elif mode == 'blank': # Remember there's still padding. + expected = "+---------+\n" "| Avocado |\n" "+---------+" + elif mode == "blank": # Remember there's still padding. if bare: - expected = ( - '' - ) + expected = "" else: - expected = ( - '+--+\n' - '| |\n' - '+--+' - ) - elif mode == 'empty': + expected = "+--+\n" "| |\n" "+--+" + elif mode == "empty": if bare: - expected = ( - '' - ) + expected = "" else: - expected = ( - '++\n' - '||\n' - '++' - ) + expected = "++\n" "||\n" "++" else: if bare: - expected = ( - '' - ) + expected = "" else: - expected = ( - '++\n' - '++' - ) + expected = "++\n" "++" assert actual == expected diff --git a/tests/test_base_table/test_horizontal_border.py b/tests/test_base_table/test_horizontal_border.py index e162261..59ae29a 100644 --- a/tests/test_base_table/test_horizontal_border.py +++ b/tests/test_base_table/test_horizontal_border.py @@ -2,47 +2,49 @@ import pytest -from terminaltables.base_table import BaseTable -from terminaltables.width_and_alignment import max_dimensions +from terminaltables3.base_table import BaseTable +from terminaltables3.width_and_alignment import max_dimensions SINGLE_LINE = ( - ('Name', 'Color', 'Type'), - ('Avocado', 'green', 'nut'), - ('Tomato', 'red', 'fruit'), - ('Lettuce', 'green', 'vegetable'), + ("Name", "Color", "Type"), + ("Avocado", "green", "nut"), + ("Tomato", "red", "fruit"), + ("Lettuce", "green", "vegetable"), ) -@pytest.mark.parametrize('inner_column_border', [True, False]) -@pytest.mark.parametrize('style', ['top', 'bottom']) +@pytest.mark.parametrize("inner_column_border", [True, False]) +@pytest.mark.parametrize("style", ["top", "bottom"]) def test_top_bottom(inner_column_border, style): """Test top and bottom borders. :param bool inner_column_border: Passed to table class. :param str style: Passed to method. """ - table = BaseTable(SINGLE_LINE, 'Example') + table = BaseTable(SINGLE_LINE, "Example") table.inner_column_border = inner_column_border - outer_widths = max_dimensions(table.table_data, table.padding_left, table.padding_right)[2] + outer_widths = max_dimensions( + table.table_data, table.padding_left, table.padding_right + )[2] # Determine expected. - if style == 'top' and inner_column_border: - expected = '+Example--+-------+-----------+' - elif style == 'top': - expected = '+Example--------------------+' - elif style == 'bottom' and inner_column_border: - expected = '+---------+-------+-----------+' + if style == "top" and inner_column_border: + expected = "+Example--+-------+-----------+" + elif style == "top": + expected = "+Example--------------------+" + elif style == "bottom" and inner_column_border: + expected = "+---------+-------+-----------+" else: - expected = '+---------------------------+' + expected = "+---------------------------+" # Test. - actual = ''.join(table.horizontal_border(style, outer_widths)) + actual = "".join(table.horizontal_border(style, outer_widths)) assert actual == expected -@pytest.mark.parametrize('inner_column_border', [True, False]) -@pytest.mark.parametrize('outer_border', [True, False]) -@pytest.mark.parametrize('style', ['heading', 'footing']) +@pytest.mark.parametrize("inner_column_border", [True, False]) +@pytest.mark.parametrize("outer_border", [True, False]) +@pytest.mark.parametrize("style", ["heading", "footing"]) def test_heading_footing(inner_column_border, outer_border, style): """Test heading and footing borders. @@ -53,25 +55,43 @@ def test_heading_footing(inner_column_border, outer_border, style): table = BaseTable(SINGLE_LINE) table.inner_column_border = inner_column_border table.outer_border = outer_border - outer_widths = max_dimensions(table.table_data, table.padding_left, table.padding_right)[2] + outer_widths = max_dimensions( + table.table_data, table.padding_left, table.padding_right + )[2] # Determine expected. - if style == 'heading' and outer_border: - expected = '+---------+-------+-----------+' if inner_column_border else '+---------------------------+' - elif style == 'heading': - expected = '---------+-------+-----------' if inner_column_border else '---------------------------' - elif style == 'footing' and outer_border: - expected = '+---------+-------+-----------+' if inner_column_border else '+---------------------------+' + if style == "heading" and outer_border: + expected = ( + "+---------+-------+-----------+" + if inner_column_border + else "+---------------------------+" + ) + elif style == "heading": + expected = ( + "---------+-------+-----------" + if inner_column_border + else "---------------------------" + ) + elif style == "footing" and outer_border: + expected = ( + "+---------+-------+-----------+" + if inner_column_border + else "+---------------------------+" + ) else: - expected = '---------+-------+-----------' if inner_column_border else '---------------------------' + expected = ( + "---------+-------+-----------" + if inner_column_border + else "---------------------------" + ) # Test. - actual = ''.join(table.horizontal_border(style, outer_widths)) + actual = "".join(table.horizontal_border(style, outer_widths)) assert actual == expected -@pytest.mark.parametrize('inner_column_border', [True, False]) -@pytest.mark.parametrize('outer_border', [True, False]) +@pytest.mark.parametrize("inner_column_border", [True, False]) +@pytest.mark.parametrize("outer_border", [True, False]) def test_row(inner_column_border, outer_border): """Test inner borders. @@ -81,18 +101,20 @@ def test_row(inner_column_border, outer_border): table = BaseTable(SINGLE_LINE) table.inner_column_border = inner_column_border table.outer_border = outer_border - outer_widths = max_dimensions(table.table_data, table.padding_left, table.padding_right)[2] + outer_widths = max_dimensions( + table.table_data, table.padding_left, table.padding_right + )[2] # Determine expected. if inner_column_border and outer_border: - expected = '+---------+-------+-----------+' + expected = "+---------+-------+-----------+" elif inner_column_border: - expected = '---------+-------+-----------' + expected = "---------+-------+-----------" elif outer_border: - expected = '+---------------------------+' + expected = "+---------------------------+" else: - expected = '---------------------------' + expected = "---------------------------" # Test. - actual = ''.join(table.horizontal_border('row', outer_widths)) + actual = "".join(table.horizontal_border("row", outer_widths)) assert actual == expected diff --git a/tests/test_base_table/test_table.py b/tests/test_base_table/test_table.py index c5b5a89..7487308 100644 --- a/tests/test_base_table/test_table.py +++ b/tests/test_base_table/test_table.py @@ -1,32 +1,31 @@ -# coding: utf-8 """Test property in BaseTable class.""" from colorama import Fore from colorclass import Color from termcolor import colored -from terminaltables.base_table import BaseTable +from terminaltables3.base_table import BaseTable def test_ascii(): """Test with ASCII characters.""" table_data = [ - ['Name', 'Color', 'Type'], - ['Avocado', 'green', 'nut'], - ['Tomato', 'red', 'fruit'], - ['Lettuce', 'green', 'vegetable'], + ["Name", "Color", "Type"], + ["Avocado", "green", "nut"], + ["Tomato", "red", "fruit"], + ["Lettuce", "green", "vegetable"], ] table = BaseTable(table_data) actual = table.table expected = ( - '+---------+-------+-----------+\n' - '| Name | Color | Type |\n' - '+---------+-------+-----------+\n' - '| Avocado | green | nut |\n' - '| Tomato | red | fruit |\n' - '| Lettuce | green | vegetable |\n' - '+---------+-------+-----------+' + "+---------+-------+-----------+\n" + "| Name | Color | Type |\n" + "+---------+-------+-----------+\n" + "| Avocado | green | nut |\n" + "| Tomato | red | fruit |\n" + "| Lettuce | green | vegetable |\n" + "+---------+-------+-----------+" ) assert actual == expected @@ -44,13 +43,13 @@ def test_int(): actual = table.table expected = ( - '+1234567890+---+\n' - '| 100 | 10 | 1 |\n' - '+-----+----+---+\n' - '| 0 | 3 | 6 |\n' - '| 1 | 4 | 7 |\n' - '| 2 | 5 | 8 |\n' - '+-----+----+---+' + "+1234567890+---+\n" + "| 100 | 10 | 1 |\n" + "+-----+----+---+\n" + "| 0 | 3 | 6 |\n" + "| 1 | 4 | 7 |\n" + "| 2 | 5 | 8 |\n" + "+-----+----+---+" ) assert actual == expected @@ -68,13 +67,13 @@ def test_float(): actual = table.table expected = ( - '+0.12345678--+-------+\n' - '| 1.0 | 22.0 | 333.0 |\n' - '+-----+------+-------+\n' - '| 0.1 | 3.1 | 6.1 |\n' - '| 1.1 | 4.1 | 7.1 |\n' - '| 2.1 | 5.1 | 8.1 |\n' - '+-----+------+-------+' + "+0.12345678--+-------+\n" + "| 1.0 | 22.0 | 333.0 |\n" + "+-----+------+-------+\n" + "| 0.1 | 3.1 | 6.1 |\n" + "| 1.1 | 4.1 | 7.1 |\n" + "| 2.1 | 5.1 | 8.1 |\n" + "+-----+------+-------+" ) assert actual == expected @@ -92,13 +91,13 @@ def test_bool_none(): actual = table.table expected = ( - '+True---+-------+-------+\n' - '| True | False | None |\n' - '+-------+-------+-------+\n' - '| True | False | None |\n' - '| False | None | True |\n' - '| None | True | False |\n' - '+-------+-------+-------+' + "+True---+-------+-------+\n" + "| True | False | None |\n" + "+-------+-------+-------+\n" + "| True | False | None |\n" + "| False | None | True |\n" + "| None | True | False |\n" + "+-------+-------+-------+" ) assert actual == expected @@ -107,20 +106,20 @@ def test_bool_none(): def test_cjk(): """Test with CJK characters.""" table_data = [ - ['CJK'], - ['蓝色'], - ['世界你好'], + ["CJK"], + ["蓝色"], + ["世界你好"], ] table = BaseTable(table_data) actual = table.table expected = ( - '+----------+\n' - '| CJK |\n' - '+----------+\n' - '| 蓝色 |\n' - '| 世界你好 |\n' - '+----------+' + "+----------+\n" + "| CJK |\n" + "+----------+\n" + "| 蓝色 |\n" + "| 世界你好 |\n" + "+----------+" ) assert actual == expected @@ -129,20 +128,15 @@ def test_cjk(): def test_rtl(): """Test with RTL characters.""" table_data = [ - ['RTL'], - ['שלום'], - ['معرب'], + ["RTL"], + ["שלום"], + ["معرب"], ] table = BaseTable(table_data) actual = table.table expected = ( - '+------+\n' - '| RTL |\n' - '+------+\n' - '| שלום |\n' - '| معرب |\n' - '+------+' + "+------+\n" "| RTL |\n" "+------+\n" "| שלום |\n" "| معرب |\n" "+------+" ) assert actual == expected @@ -151,22 +145,22 @@ def test_rtl(): def test_rtl_large(): """Test large table of RTL characters.""" table_data = [ - ['اكتب', 'اللون', 'اسم'], - ['البندق', 'أخضر', 'أفوكادو'], - ['ثمرة', 'أحمر', 'بندورة'], - ['الخضروات', 'أخضر', 'الخس'], + ["اكتب", "اللون", "اسم"], + ["البندق", "أخضر", "أفوكادو"], + ["ثمرة", "أحمر", "بندورة"], + ["الخضروات", "أخضر", "الخس"], ] - table = BaseTable(table_data, 'جوجل المترجم') + table = BaseTable(table_data, "جوجل المترجم") actual = table.table expected = ( - '+جوجل المترجم------+---------+\n' - '| اكتب | اللون | اسم |\n' - '+----------+-------+---------+\n' - '| البندق | أخضر | أفوكادو |\n' - '| ثمرة | أحمر | بندورة |\n' - '| الخضروات | أخضر | الخس |\n' - '+----------+-------+---------+' + "+جوجل المترجم------+---------+\n" + "| اكتب | اللون | اسم |\n" + "+----------+-------+---------+\n" + "| البندق | أخضر | أفوكادو |\n" + "| ثمرة | أحمر | بندورة |\n" + "| الخضروات | أخضر | الخس |\n" + "+----------+-------+---------+" ) assert actual == expected @@ -175,22 +169,42 @@ def test_rtl_large(): def test_color(): """Test with color characters.""" table_data = [ - ['ansi', '\033[31mRed\033[39m', '\033[32mGreen\033[39m', '\033[34mBlue\033[39m'], - ['colorclass', Color('{red}Red{/red}'), Color('{green}Green{/green}'), Color('{blue}Blue{/blue}')], - ['colorama', Fore.RED + 'Red' + Fore.RESET, Fore.GREEN + 'Green' + Fore.RESET, Fore.BLUE + 'Blue' + Fore.RESET], - ['termcolor', colored('Red', 'red'), colored('Green', 'green'), colored('Blue', 'blue')], + [ + "ansi", + "\033[31mRed\033[39m", + "\033[32mGreen\033[39m", + "\033[34mBlue\033[39m", + ], + [ + "colorclass", + Color("{red}Red{/red}"), + Color("{green}Green{/green}"), + Color("{blue}Blue{/blue}"), + ], + [ + "colorama", + Fore.RED + "Red" + Fore.RESET, + Fore.GREEN + "Green" + Fore.RESET, + Fore.BLUE + "Blue" + Fore.RESET, + ], + [ + "termcolor", + colored("Red", "red"), + colored("Green", "green"), + colored("Blue", "blue"), + ], ] table = BaseTable(table_data) table.inner_heading_row_border = False actual = table.table expected = ( - u'+------------+-----+-------+------+\n' - u'| ansi | \033[31mRed\033[39m | \033[32mGreen\033[39m | \033[34mBlue\033[39m |\n' - u'| colorclass | \033[31mRed\033[39m | \033[32mGreen\033[39m | \033[34mBlue\033[39m |\n' - u'| colorama | \033[31mRed\033[39m | \033[32mGreen\033[39m | \033[34mBlue\033[39m |\n' - u'| termcolor | \033[31mRed\033[0m | \033[32mGreen\033[0m | \033[34mBlue\033[0m |\n' - u'+------------+-----+-------+------+' + "+------------+-----+-------+------+\n" + "| ansi | \033[31mRed\033[39m | \033[32mGreen\033[39m | \033[34mBlue\033[39m |\n" + "| colorclass | \033[31mRed\033[39m | \033[32mGreen\033[39m | \033[34mBlue\033[39m |\n" + "| colorama | \033[31mRed\033[39m | \033[32mGreen\033[39m | \033[34mBlue\033[39m |\n" + "| termcolor | \033[31mRed\033[0m | \033[32mGreen\033[0m | \033[34mBlue\033[0m |\n" + "+------------+-----+-------+------+" ) assert actual == expected diff --git a/tests/test_build/__init__.py b/tests/test_build/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/test_build/__init__.py diff --git a/tests/test_build/test_build_border.py b/tests/test_build/test_build_border.py index 9c410fd..1ac2d0a 100644 --- a/tests/test_build/test_build_border.py +++ b/tests/test_build/test_build_border.py @@ -1,4 +1,3 @@ -# coding: utf-8 """Test function in module.""" import pytest @@ -6,16 +5,19 @@ from colorama import Fore, Style from colorclass import Color from termcolor import colored -from terminaltables.build import build_border +from terminaltables3.build import build_border -@pytest.mark.parametrize('outer_widths,horizontal,left,intersect,right,expected', [ - ([5, 6, 7], '-', '<', '+', '>', '<-----+------+------->'), - ([1, 1, 1], '-', '', '', '', '---'), - ([1, 1, 1], '', '', '', '', ''), - ([1], '-', '<', '+', '>', '<->'), - ([], '-', '<', '+', '>', '<>'), -]) +@pytest.mark.parametrize( + "outer_widths,horizontal,left,intersect,right,expected", + [ + ([5, 6, 7], "-", "<", "+", ">", "<-----+------+------->"), + ([1, 1, 1], "-", "", "", "", "---"), + ([1, 1, 1], "", "", "", "", ""), + ([1], "-", "<", "+", ">", "<->"), + ([], "-", "<", "+", ">", "<>"), + ], +) def test_no_title(outer_widths, horizontal, left, intersect, right, expected): """Test without title. @@ -27,26 +29,25 @@ def test_no_title(outer_widths, horizontal, left, intersect, right, expected): :param str expected: Expected output. """ actual = build_border(outer_widths, horizontal, left, intersect, right) - assert ''.join(actual) == expected - - -@pytest.mark.parametrize('outer_widths,intersect,expected', [ - ([20], '+', 'Applications--------'), - ([20], '', 'Applications--------'), - - ([15, 5], '+', 'Applications---+-----'), - ([15, 5], '', 'Applications--------'), - - ([12], '+', 'Applications'), - ([12], '', 'Applications'), - - ([12, 1], '+', 'Applications+-'), - ([12, 1], '', 'Applications-'), - - ([12, 0], '+', 'Applications+'), - ([12, 0], '', 'Applications'), -]) -@pytest.mark.parametrize('left,right', [('', ''), ('<', '>')]) + assert "".join(actual) == expected + + +@pytest.mark.parametrize( + "outer_widths,intersect,expected", + [ + ([20], "+", "Applications--------"), + ([20], "", "Applications--------"), + ([15, 5], "+", "Applications---+-----"), + ([15, 5], "", "Applications--------"), + ([12], "+", "Applications"), + ([12], "", "Applications"), + ([12, 1], "+", "Applications+-"), + ([12, 1], "", "Applications-"), + ([12, 0], "+", "Applications+"), + ([12, 0], "", "Applications"), + ], +) +@pytest.mark.parametrize("left,right", [("", ""), ("<", ">")]) def test_first_column_fit(outer_widths, left, intersect, right, expected): """Test with title that fits in the first column. @@ -58,23 +59,28 @@ def test_first_column_fit(outer_widths, left, intersect, right, expected): """ if left and right: expected = left + expected + right - actual = build_border(outer_widths, '-', left, intersect, right, title='Applications') - assert ''.join(actual) == expected - - -@pytest.mark.parametrize('outer_widths,expected', [ - ([20], 'Applications--------'), - ([10, 10], 'Applications--------'), - ([5, 5, 5, 5], 'Applications--------'), - ([3, 2, 3, 2, 3, 2, 3, 2], 'Applications--------'), - ([1] * 20, 'Applications--------'), - ([10, 5], 'Applications---'), - ([9, 5], 'Applications--'), - ([8, 5], 'Applications-'), - ([7, 5], 'Applications'), - ([6, 5], '-----------'), -]) -@pytest.mark.parametrize('left,right', [('', ''), ('<', '>')]) + actual = build_border( + outer_widths, "-", left, intersect, right, title="Applications" + ) + assert "".join(actual) == expected + + +@pytest.mark.parametrize( + "outer_widths,expected", + [ + ([20], "Applications--------"), + ([10, 10], "Applications--------"), + ([5, 5, 5, 5], "Applications--------"), + ([3, 2, 3, 2, 3, 2, 3, 2], "Applications--------"), + ([1] * 20, "Applications--------"), + ([10, 5], "Applications---"), + ([9, 5], "Applications--"), + ([8, 5], "Applications-"), + ([7, 5], "Applications"), + ([6, 5], "-----------"), + ], +) +@pytest.mark.parametrize("left,right", [("", ""), ("<", ">")]) def test_no_intersect(outer_widths, left, right, expected): """Test with no column dividers. @@ -85,37 +91,39 @@ def test_no_intersect(outer_widths, left, right, expected): """ if left and right: expected = left + expected + right - actual = build_border(outer_widths, '-', left, '', right, title='Applications') - assert ''.join(actual) == expected - - -@pytest.mark.parametrize('outer_widths,expected', [ - ([20], 'Applications--------'), - ([0, 20], 'Applications---------'), - ([20, 0], 'Applications--------+'), - ([0, 0, 20], 'Applications----------'), - ([20, 0, 0], 'Applications--------++'), - - ([10, 10], 'Applications---------'), - ([11, 9], 'Applications---------'), - ([12, 8], 'Applications+--------'), - ([13, 7], 'Applications-+-------'), - - ([5, 5, 5, 5], 'Applications-----+-----'), - ([4, 4, 6, 6], 'Applications----+------'), - ([3, 3, 7, 7], 'Applications---+-------'), - ([2, 2, 7, 9], 'Applications-+---------'), - ([1, 1, 9, 9], 'Applications-+---------'), - - ([2, 2, 2, 2, 2, 2, 2], 'Applications--+--+--'), - ([1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 'Applications-+-+-+-'), - ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'Applications++++++++'), - - ([2, 2, 2, 2], '--+--+--+--'), - ([1, 1, 1, 1, 1], '-+-+-+-+-'), - ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0], '+++++++++'), -]) -@pytest.mark.parametrize('left,right', [('', ''), ('<', '>')]) + actual = build_border(outer_widths, "-", left, "", right, title="Applications") + assert "".join(actual) == expected + + +@pytest.mark.parametrize( + "outer_widths,expected", + [ + ([20], "Applications--------"), + ([0, 20], "Applications---------"), + ([20, 0], "Applications--------+"), + ([0, 0, 20], "Applications----------"), + ([20, 0, 0], "Applications--------++"), + ([10, 10], "Applications---------"), + ([11, 9], "Applications---------"), + ([12, 8], "Applications+--------"), + ([13, 7], "Applications-+-------"), + ([5, 5, 5, 5], "Applications-----+-----"), + ([4, 4, 6, 6], "Applications----+------"), + ([3, 3, 7, 7], "Applications---+-------"), + ([2, 2, 7, 9], "Applications-+---------"), + ([1, 1, 9, 9], "Applications-+---------"), + ([2, 2, 2, 2, 2, 2, 2], "Applications--+--+--"), + ([1, 1, 1, 1, 1, 1, 1, 1, 1, 1], "Applications-+-+-+-"), + ( + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "Applications++++++++", + ), + ([2, 2, 2, 2], "--+--+--+--"), + ([1, 1, 1, 1, 1], "-+-+-+-+-"), + ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "+++++++++"), + ], +) +@pytest.mark.parametrize("left,right", [("", ""), ("<", ">")]) def test_intersect(outer_widths, left, right, expected): """Test with column dividers. @@ -126,48 +134,51 @@ def test_intersect(outer_widths, left, right, expected): """ if left and right: expected = left + expected + right - actual = build_border(outer_widths, '-', left, '+', right, title='Applications') - assert ''.join(actual) == expected - - -@pytest.mark.parametrize('outer_widths,intersect,expected', [ - ([12], '+', u'蓝色--------'), - ([12], '', u'蓝色--------'), - ([7, 5], '+', u'蓝色---+-----'), - ([7, 5], '', u'蓝色--------'), - ([4], '+', u'蓝色'), - ([4], '', u'蓝色'), - ([4, 1], '+', u'蓝色+-'), - ([4, 1], '', u'蓝色-'), - ([4, 0], '+', u'蓝色+'), - ([4, 0], '', u'蓝色'), - ([12], '', u'蓝色--------'), - ([6, 6], '', u'蓝色--------'), - ([3, 3, 3, 3], '', u'蓝色--------'), - ([2, 1, 2, 1, 2, 1, 2, 1], '', u'蓝色--------'), - ([1] * 12, '', u'蓝色--------'), - ([2, 4], '', u'蓝色--'), - ([1, 4], '', u'蓝色-'), - ([1, 3], '', u'蓝色'), - ([1, 2], '', u'---'), - ([2], '', u'--'), - ([12], '+', u'蓝色--------'), - ([0, 12], '+', u'蓝色---------'), - ([12, 0], '+', u'蓝色--------+'), - ([0, 0, 12], '+', u'蓝色----------'), - ([12, 0, 0], '+', u'蓝色--------++'), - ([3, 3], '+', u'蓝色---'), - ([4, 2], '+', u'蓝色+--'), - ([5, 1], '+', u'蓝色-+-'), - ([3, 3, 3, 3], '+', u'蓝色---+---+---'), - ([2, 2, 4, 4], '+', u'蓝色-+----+----'), - ([1, 1, 5, 5], '+', u'蓝色-----+-----'), - ([2, 2, 2, 2], '+', u'蓝色-+--+--'), - ([1, 1, 1, 1, 1], '+', u'蓝色-+-+-'), - ([0, 0, 0, 0, 0, 0, 0], '+', u'蓝色++'), - ([1, 1], '+', u'-+-'), -]) -@pytest.mark.parametrize('left,right', [('', ''), ('<', '>')]) + actual = build_border(outer_widths, "-", left, "+", right, title="Applications") + assert "".join(actual) == expected + + +@pytest.mark.parametrize( + "outer_widths,intersect,expected", + [ + ([12], "+", "蓝色--------"), + ([12], "", "蓝色--------"), + ([7, 5], "+", "蓝色---+-----"), + ([7, 5], "", "蓝色--------"), + ([4], "+", "蓝色"), + ([4], "", "蓝色"), + ([4, 1], "+", "蓝色+-"), + ([4, 1], "", "蓝色-"), + ([4, 0], "+", "蓝色+"), + ([4, 0], "", "蓝色"), + ([12], "", "蓝色--------"), + ([6, 6], "", "蓝色--------"), + ([3, 3, 3, 3], "", "蓝色--------"), + ([2, 1, 2, 1, 2, 1, 2, 1], "", "蓝色--------"), + ([1] * 12, "", "蓝色--------"), + ([2, 4], "", "蓝色--"), + ([1, 4], "", "蓝色-"), + ([1, 3], "", "蓝色"), + ([1, 2], "", "---"), + ([2], "", "--"), + ([12], "+", "蓝色--------"), + ([0, 12], "+", "蓝色---------"), + ([12, 0], "+", "蓝色--------+"), + ([0, 0, 12], "+", "蓝色----------"), + ([12, 0, 0], "+", "蓝色--------++"), + ([3, 3], "+", "蓝色---"), + ([4, 2], "+", "蓝色+--"), + ([5, 1], "+", "蓝色-+-"), + ([3, 3, 3, 3], "+", "蓝色---+---+---"), + ([2, 2, 4, 4], "+", "蓝色-+----+----"), + ([1, 1, 5, 5], "+", "蓝色-----+-----"), + ([2, 2, 2, 2], "+", "蓝色-+--+--"), + ([1, 1, 1, 1, 1], "+", "蓝色-+-+-"), + ([0, 0, 0, 0, 0, 0, 0], "+", "蓝色++"), + ([1, 1], "+", "-+-"), + ], +) +@pytest.mark.parametrize("left,right", [("", ""), ("<", ">")]) def test_cjk(outer_widths, left, intersect, right, expected): """Test with CJK characters in title. @@ -179,48 +190,51 @@ def test_cjk(outer_widths, left, intersect, right, expected): """ if left and right: expected = left + expected + right - actual = build_border(outer_widths, '-', left, intersect, right, title=u'蓝色') - assert ''.join(actual) == expected - - -@pytest.mark.parametrize('outer_widths,intersect,expected', [ - ([12], '+', u'معرب--------'), - ([12], '', u'معرب--------'), - ([7, 5], '+', u'معرب---+-----'), - ([7, 5], '', u'معرب--------'), - ([4], '+', u'معرب'), - ([4], '', u'معرب'), - ([4, 1], '+', u'معرب+-'), - ([4, 1], '', u'معرب-'), - ([4, 0], '+', u'معرب+'), - ([4, 0], '', u'معرب'), - ([12], '', u'معرب--------'), - ([6, 6], '', u'معرب--------'), - ([3, 3, 3, 3], '', u'معرب--------'), - ([2, 1, 2, 1, 2, 1, 2, 1], '', u'معرب--------'), - ([1] * 12, '', u'معرب--------'), - ([2, 4], '', u'معرب--'), - ([1, 4], '', u'معرب-'), - ([1, 3], '', u'معرب'), - ([1, 2], '', u'---'), - ([2], '', u'--'), - ([12], '+', u'معرب--------'), - ([0, 12], '+', u'معرب---------'), - ([12, 0], '+', u'معرب--------+'), - ([0, 0, 12], '+', u'معرب----------'), - ([12, 0, 0], '+', u'معرب--------++'), - ([3, 3], '+', u'معرب---'), - ([4, 2], '+', u'معرب+--'), - ([5, 1], '+', u'معرب-+-'), - ([3, 3, 3, 3], '+', u'معرب---+---+---'), - ([2, 2, 4, 4], '+', u'معرب-+----+----'), - ([1, 1, 5, 5], '+', u'معرب-----+-----'), - ([2, 2, 2, 2], '+', u'معرب-+--+--'), - ([1, 1, 1, 1, 1], '+', u'معرب-+-+-'), - ([0, 0, 0, 0, 0, 0, 0], '+', u'معرب++'), - ([1, 1], '+', u'-+-'), -]) -@pytest.mark.parametrize('left,right', [('', ''), ('<', '>')]) + actual = build_border(outer_widths, "-", left, intersect, right, title="蓝色") + assert "".join(actual) == expected + + +@pytest.mark.parametrize( + "outer_widths,intersect,expected", + [ + ([12], "+", "معرب--------"), + ([12], "", "معرب--------"), + ([7, 5], "+", "معرب---+-----"), + ([7, 5], "", "معرب--------"), + ([4], "+", "معرب"), + ([4], "", "معرب"), + ([4, 1], "+", "معرب+-"), + ([4, 1], "", "معرب-"), + ([4, 0], "+", "معرب+"), + ([4, 0], "", "معرب"), + ([12], "", "معرب--------"), + ([6, 6], "", "معرب--------"), + ([3, 3, 3, 3], "", "معرب--------"), + ([2, 1, 2, 1, 2, 1, 2, 1], "", "معرب--------"), + ([1] * 12, "", "معرب--------"), + ([2, 4], "", "معرب--"), + ([1, 4], "", "معرب-"), + ([1, 3], "", "معرب"), + ([1, 2], "", "---"), + ([2], "", "--"), + ([12], "+", "معرب--------"), + ([0, 12], "+", "معرب---------"), + ([12, 0], "+", "معرب--------+"), + ([0, 0, 12], "+", "معرب----------"), + ([12, 0, 0], "+", "معرب--------++"), + ([3, 3], "+", "معرب---"), + ([4, 2], "+", "معرب+--"), + ([5, 1], "+", "معرب-+-"), + ([3, 3, 3, 3], "+", "معرب---+---+---"), + ([2, 2, 4, 4], "+", "معرب-+----+----"), + ([1, 1, 5, 5], "+", "معرب-----+-----"), + ([2, 2, 2, 2], "+", "معرب-+--+--"), + ([1, 1, 1, 1, 1], "+", "معرب-+-+-"), + ([0, 0, 0, 0, 0, 0, 0], "+", "معرب++"), + ([1, 1], "+", "-+-"), + ], +) +@pytest.mark.parametrize("left,right", [("", ""), ("<", ">")]) def test_rtl(outer_widths, left, intersect, right, expected): """Test with RTL characters in title. @@ -232,53 +246,59 @@ def test_rtl(outer_widths, left, intersect, right, expected): """ if left and right: expected = left + expected + right - actual = build_border(outer_widths, '-', left, intersect, right, title=u'معرب') - assert ''.join(actual) == expected - - -@pytest.mark.parametrize('outer_widths,intersect,expected', [ - ([12], '+', '\x1b[34mTEST\x1b[0m--------'), - ([12], '', '\x1b[34mTEST\x1b[0m--------'), - ([7, 5], '+', '\x1b[34mTEST\x1b[0m---+-----'), - ([7, 5], '', '\x1b[34mTEST\x1b[0m--------'), - ([4], '+', '\x1b[34mTEST\x1b[0m'), - ([4], '', '\x1b[34mTEST\x1b[0m'), - ([4, 1], '+', '\x1b[34mTEST\x1b[0m+-'), - ([4, 1], '', '\x1b[34mTEST\x1b[0m-'), - ([4, 0], '+', '\x1b[34mTEST\x1b[0m+'), - ([4, 0], '', '\x1b[34mTEST\x1b[0m'), - ([12], '', '\x1b[34mTEST\x1b[0m--------'), - ([6, 6], '', '\x1b[34mTEST\x1b[0m--------'), - ([3, 3, 3, 3], '', '\x1b[34mTEST\x1b[0m--------'), - ([2, 1, 2, 1, 2, 1, 2, 1], '', '\x1b[34mTEST\x1b[0m--------'), - ([1] * 12, '', '\x1b[34mTEST\x1b[0m--------'), - ([2, 4], '', '\x1b[34mTEST\x1b[0m--'), - ([1, 4], '', '\x1b[34mTEST\x1b[0m-'), - ([1, 3], '', '\x1b[34mTEST\x1b[0m'), - ([1, 2], '', '---'), - ([12], '+', '\x1b[34mTEST\x1b[0m--------'), - ([0, 12], '+', '\x1b[34mTEST\x1b[0m---------'), - ([12, 0], '+', '\x1b[34mTEST\x1b[0m--------+'), - ([0, 0, 12], '+', '\x1b[34mTEST\x1b[0m----------'), - ([12, 0, 0], '+', '\x1b[34mTEST\x1b[0m--------++'), - ([3, 3], '+', '\x1b[34mTEST\x1b[0m---'), - ([4, 2], '+', '\x1b[34mTEST\x1b[0m+--'), - ([5, 1], '+', '\x1b[34mTEST\x1b[0m-+-'), - ([3, 3, 3, 3], '+', '\x1b[34mTEST\x1b[0m---+---+---'), - ([2, 2, 4, 4], '+', '\x1b[34mTEST\x1b[0m-+----+----'), - ([1, 1, 5, 5], '+', '\x1b[34mTEST\x1b[0m-----+-----'), - ([2, 2, 2, 2], '+', '\x1b[34mTEST\x1b[0m-+--+--'), - ([1, 1, 1, 1, 1], '+', '\x1b[34mTEST\x1b[0m-+-+-'), - ([0, 0, 0, 0, 0, 0, 0], '+', '\x1b[34mTEST\x1b[0m++'), - ([1, 1], '+', '-+-'), -]) -@pytest.mark.parametrize('left,right', [('', ''), ('<', '>')]) -@pytest.mark.parametrize('title', [ - '\x1b[34mTEST\x1b[0m', - Color('{blue}TEST{/all}'), - Fore.BLUE + 'TEST' + Style.RESET_ALL, - colored('TEST', 'blue'), -]) + actual = build_border(outer_widths, "-", left, intersect, right, title="معرب") + assert "".join(actual) == expected + + +@pytest.mark.parametrize( + "outer_widths,intersect,expected", + [ + ([12], "+", "\x1b[34mTEST\x1b[0m--------"), + ([12], "", "\x1b[34mTEST\x1b[0m--------"), + ([7, 5], "+", "\x1b[34mTEST\x1b[0m---+-----"), + ([7, 5], "", "\x1b[34mTEST\x1b[0m--------"), + ([4], "+", "\x1b[34mTEST\x1b[0m"), + ([4], "", "\x1b[34mTEST\x1b[0m"), + ([4, 1], "+", "\x1b[34mTEST\x1b[0m+-"), + ([4, 1], "", "\x1b[34mTEST\x1b[0m-"), + ([4, 0], "+", "\x1b[34mTEST\x1b[0m+"), + ([4, 0], "", "\x1b[34mTEST\x1b[0m"), + ([12], "", "\x1b[34mTEST\x1b[0m--------"), + ([6, 6], "", "\x1b[34mTEST\x1b[0m--------"), + ([3, 3, 3, 3], "", "\x1b[34mTEST\x1b[0m--------"), + ([2, 1, 2, 1, 2, 1, 2, 1], "", "\x1b[34mTEST\x1b[0m--------"), + ([1] * 12, "", "\x1b[34mTEST\x1b[0m--------"), + ([2, 4], "", "\x1b[34mTEST\x1b[0m--"), + ([1, 4], "", "\x1b[34mTEST\x1b[0m-"), + ([1, 3], "", "\x1b[34mTEST\x1b[0m"), + ([1, 2], "", "---"), + ([12], "+", "\x1b[34mTEST\x1b[0m--------"), + ([0, 12], "+", "\x1b[34mTEST\x1b[0m---------"), + ([12, 0], "+", "\x1b[34mTEST\x1b[0m--------+"), + ([0, 0, 12], "+", "\x1b[34mTEST\x1b[0m----------"), + ([12, 0, 0], "+", "\x1b[34mTEST\x1b[0m--------++"), + ([3, 3], "+", "\x1b[34mTEST\x1b[0m---"), + ([4, 2], "+", "\x1b[34mTEST\x1b[0m+--"), + ([5, 1], "+", "\x1b[34mTEST\x1b[0m-+-"), + ([3, 3, 3, 3], "+", "\x1b[34mTEST\x1b[0m---+---+---"), + ([2, 2, 4, 4], "+", "\x1b[34mTEST\x1b[0m-+----+----"), + ([1, 1, 5, 5], "+", "\x1b[34mTEST\x1b[0m-----+-----"), + ([2, 2, 2, 2], "+", "\x1b[34mTEST\x1b[0m-+--+--"), + ([1, 1, 1, 1, 1], "+", "\x1b[34mTEST\x1b[0m-+-+-"), + ([0, 0, 0, 0, 0, 0, 0], "+", "\x1b[34mTEST\x1b[0m++"), + ([1, 1], "+", "-+-"), + ], +) +@pytest.mark.parametrize("left,right", [("", ""), ("<", ">")]) +@pytest.mark.parametrize( + "title", + [ + "\x1b[34mTEST\x1b[0m", + Color("{blue}TEST{/all}"), + Fore.BLUE + "TEST" + Style.RESET_ALL, + colored("TEST", "blue"), + ], +) def test_colors(outer_widths, left, intersect, right, title, expected): """Test with color title characters. @@ -291,16 +311,19 @@ def test_colors(outer_widths, left, intersect, right, title, expected): """ if left and right: expected = left + expected + right - actual = build_border(outer_widths, '-', left, intersect, right, title=title) - assert ''.join(actual) == expected - - -@pytest.mark.parametrize('outer_widths,title,expected', [ - ([3, 3, 3], 123, '<123+---+--->'), - ([3, 3, 3], 0.9, '<0.9+---+--->'), - ([3, 3, 3], True, '<True---+--->'), - ([3, 3, 3], False, '<False--+--->'), -]) + actual = build_border(outer_widths, "-", left, intersect, right, title=title) + assert "".join(actual) == expected + + +@pytest.mark.parametrize( + "outer_widths,title,expected", + [ + ([3, 3, 3], 123, "<123+---+--->"), + ([3, 3, 3], 0.9, "<0.9+---+--->"), + ([3, 3, 3], True, "<True---+--->"), + ([3, 3, 3], False, "<False--+--->"), + ], +) def test_non_string(outer_widths, title, expected): """Test with non-string values. @@ -308,5 +331,5 @@ def test_non_string(outer_widths, title, expected): :param title: Title in border. :param str expected: Expected output. """ - actual = build_border(outer_widths, '-', '<', '+', '>', title=title) - assert ''.join(actual) == expected + actual = build_border(outer_widths, "-", "<", "+", ">", title=title) + assert "".join(actual) == expected diff --git a/tests/test_build/test_build_row.py b/tests/test_build/test_build_row.py index ce55944..93aaf37 100644 --- a/tests/test_build/test_build_row.py +++ b/tests/test_build/test_build_row.py @@ -1,16 +1,18 @@ """Test function in module.""" -from terminaltables.build import build_row +from terminaltables3.build import build_row def test_one_line(): """Test with one line cells.""" row = [ - ['Left Cell'], ['Center Cell'], ['Right Cell'], + ["Left Cell"], + ["Center Cell"], + ["Right Cell"], ] - actual = [tuple(i) for i in build_row(row, '>', '|', '<')] + actual = [tuple(i) for i in build_row(row, ">", "|", "<")] expected = [ - ('>', 'Left Cell', '|', 'Center Cell', '|', 'Right Cell', '<'), + (">", "Left Cell", "|", "Center Cell", "|", "Right Cell", "<"), ] assert actual == expected @@ -19,24 +21,22 @@ def test_two_line(): """Test with two line cells.""" row = [ [ - 'Left ', - 'Cell1', + "Left ", + "Cell1", ], - [ - 'Center', - 'Cell2 ', + "Center", + "Cell2 ", ], - [ - 'Right', - 'Cell3', + "Right", + "Cell3", ], ] - actual = [tuple(i) for i in build_row(row, '>', '|', '<')] + actual = [tuple(i) for i in build_row(row, ">", "|", "<")] expected = [ - ('>', 'Left ', '|', 'Center', '|', 'Right', '<'), - ('>', 'Cell1', '|', 'Cell2 ', '|', 'Cell3', '<'), + (">", "Left ", "|", "Center", "|", "Right", "<"), + (">", "Cell1", "|", "Cell2 ", "|", "Cell3", "<"), ] assert actual == expected @@ -45,60 +45,58 @@ def test_three_line(): """Test with three line cells.""" row = [ [ - 'Left ', - 'Cell1', - ' ', + "Left ", + "Cell1", + " ", ], - [ - 'Center', - 'Cell2 ', - ' ', + "Center", + "Cell2 ", + " ", ], - [ - 'Right', - 'Cell3', - ' ', + "Right", + "Cell3", + " ", ], ] - actual = [tuple(i) for i in build_row(row, '>', '|', '<')] + actual = [tuple(i) for i in build_row(row, ">", "|", "<")] expected = [ - ('>', 'Left ', '|', 'Center', '|', 'Right', '<'), - ('>', 'Cell1', '|', 'Cell2 ', '|', 'Cell3', '<'), - ('>', ' ', '|', ' ', '|', ' ', '<'), + (">", "Left ", "|", "Center", "|", "Right", "<"), + (">", "Cell1", "|", "Cell2 ", "|", "Cell3", "<"), + (">", " ", "|", " ", "|", " ", "<"), ] assert actual == expected def test_single(): """Test with single cell.""" - actual = [tuple(i) for i in build_row([['Cell']], '>', '|', '<')] + actual = [tuple(i) for i in build_row([["Cell"]], ">", "|", "<")] expected = [ - ('>', 'Cell', '<'), + (">", "Cell", "<"), ] assert actual == expected def test_empty(): """Test with empty cell.""" - actual = [tuple(i) for i in build_row([['']], '>', '|', '<')] + actual = [tuple(i) for i in build_row([[""]], ">", "|", "<")] expected = [ - ('>', '', '<'), + (">", "", "<"), ] assert actual == expected def test_no_cells(): """Test with no cells.""" - actual = [tuple(i) for i in build_row([[]], '>', '|', '<')] + actual = [tuple(i) for i in build_row([[]], ">", "|", "<")] expected = [ - ('>', '<'), + (">", "<"), ] assert actual == expected - actual = [tuple(i) for i in build_row([], '>', '|', '<')] + actual = [tuple(i) for i in build_row([], ">", "|", "<")] expected = [ - ('>', '<'), + (">", "<"), ] assert actual == expected diff --git a/tests/test_build/test_combine.py b/tests/test_build/test_combine.py index b296ffd..a96d302 100644 --- a/tests/test_build/test_combine.py +++ b/tests/test_build/test_combine.py @@ -2,36 +2,36 @@ import pytest -from terminaltables.build import combine +from terminaltables3.build import combine -@pytest.mark.parametrize('generator', [False, True]) +@pytest.mark.parametrize("generator", [False, True]) def test_borders(generator): """Test with borders. :param bool generator: Test with generator instead of list. """ - line = ['One', 'Two', 'Three'] - actual = list(combine(iter(line) if generator else line, '>', '|', '<')) - assert actual == ['>', 'One', '|', 'Two', '|', 'Three', '<'] + line = ["One", "Two", "Three"] + actual = list(combine(iter(line) if generator else line, ">", "|", "<")) + assert actual == [">", "One", "|", "Two", "|", "Three", "<"] -@pytest.mark.parametrize('generator', [False, True]) +@pytest.mark.parametrize("generator", [False, True]) def test_no_border(generator): """Test without borders. :param bool generator: Test with generator instead of list. """ - line = ['One', 'Two', 'Three'] - actual = list(combine(iter(line) if generator else line, '', '', '')) - assert actual == ['One', 'Two', 'Three'] + line = ["One", "Two", "Three"] + actual = list(combine(iter(line) if generator else line, "", "", "")) + assert actual == ["One", "Two", "Three"] -@pytest.mark.parametrize('generator', [False, True]) +@pytest.mark.parametrize("generator", [False, True]) def test_no_items(generator): """Test with empty list. :param bool generator: Test with generator instead of list. """ - actual = list(combine(iter([]) if generator else [], '>', '|', '<')) - assert actual == ['>', '<'] + actual = list(combine(iter([]) if generator else [], ">", "|", "<")) + assert actual == [">", "<"] diff --git a/tests/test_build/test_flatten.py b/tests/test_build/test_flatten.py index aacfdbd..44e1e18 100644 --- a/tests/test_build/test_flatten.py +++ b/tests/test_build/test_flatten.py @@ -1,25 +1,24 @@ """Test function in module.""" -from terminaltables.build import flatten +from terminaltables3.build import flatten def test_one_line(): """Test with one line cells.""" table = [ - ['>', 'Left Cell', '|', 'Center Cell', '|', 'Right Cell', '<'], + [">", "Left Cell", "|", "Center Cell", "|", "Right Cell", "<"], ] actual = flatten(table) - expected = '>Left Cell|Center Cell|Right Cell<' + expected = ">Left Cell|Center Cell|Right Cell<" assert actual == expected def test_two_line(): """Test with two line cells.""" table = [ - ['>', 'Left ', '|', 'Center', '|', 'Right', '<'], - ['>', 'Cell1', '|', 'Cell2 ', '|', 'Cell3', '<'], + [">", "Left ", "|", "Center", "|", "Right", "<"], + [">", "Cell1", "|", "Cell2 ", "|", "Cell3", "<"], ] actual = flatten(table) - expected = ('>Left |Center|Right<\n' - '>Cell1|Cell2 |Cell3<') + expected = ">Left |Center|Right<\n" ">Cell1|Cell2 |Cell3<" assert actual == expected diff --git a/tests/test_examples.py b/tests/test_examples.py index f0799f9..d882890 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -1,7 +1,5 @@ """Test example scripts.""" -from __future__ import print_function - import os import subprocess import sys @@ -11,17 +9,19 @@ import pytest from tests import PROJECT_ROOT -@pytest.mark.parametrize('filename', map('example{0}.py'.format, (1, 2, 3))) +@pytest.mark.parametrize("filename", map("example{}.py".format, (1, 2, 3))) def test(filename): """Test with subprocess. :param str filename: Example script filename to run. """ command = [sys.executable, str(PROJECT_ROOT.join(filename))] - env = dict(os.environ, PYTHONIOENCODING='utf-8') + env = dict(os.environ, PYTHONIOENCODING="utf-8") # Run. - proc = subprocess.Popen(command, env=env, stderr=subprocess.STDOUT, stdout=subprocess.PIPE) + proc = subprocess.Popen( + command, env=env, stderr=subprocess.STDOUT, stdout=subprocess.PIPE + ) output = proc.communicate()[0] # Verify. diff --git a/tests/test_terminal_io/__init__.py b/tests/test_terminal_io/__init__.py index 93738fc..f8200f0 100644 --- a/tests/test_terminal_io/__init__.py +++ b/tests/test_terminal_io/__init__.py @@ -1,17 +1,21 @@ """Common objects used by tests in directory.""" -from terminaltables import terminal_io +from terminaltables3 import terminal_io -class MockKernel32(object): +class MockKernel32: """Mock kernel32.""" - def __init__(self, stderr=terminal_io.INVALID_HANDLE_VALUE, stdout=terminal_io.INVALID_HANDLE_VALUE): + def __init__( + self, + stderr=terminal_io.INVALID_HANDLE_VALUE, + stdout=terminal_io.INVALID_HANDLE_VALUE, + ): """Constructor.""" self.stderr = stderr self.stdout = stdout - self.csbi_err = b'x\x00)#\x00\x00\x87\x05\x07\x00\x00\x00j\x05w\x00\x87\x05x\x00J\x00' # 119 x 29 - self.csbi_out = b'L\x00,\x01\x00\x00*\x01\x07\x00\x00\x00\x0e\x01K\x00*\x01L\x00L\x00' # 75 x 28 + self.csbi_err = b"x\x00)#\x00\x00\x87\x05\x07\x00\x00\x00j\x05w\x00\x87\x05x\x00J\x00" # 119 x 29 + self.csbi_out = b"L\x00,\x01\x00\x00*\x01\x07\x00\x00\x00\x0e\x01K\x00*\x01L\x00L\x00" # 75 x 28 self.setConsoleTitleA_called = False self.setConsoleTitleW_called = False diff --git a/tests/test_terminal_io/test_get_console_info.py b/tests/test_terminal_io/test_get_console_info.py index 1a9b98f..522613e 100644 --- a/tests/test_terminal_io/test_get_console_info.py +++ b/tests/test_terminal_io/test_get_console_info.py @@ -1,11 +1,14 @@ -# coding: utf-8 """Test function in module.""" import ctypes import pytest -from terminaltables.terminal_io import get_console_info, INVALID_HANDLE_VALUE, IS_WINDOWS +from terminaltables3.terminal_io import ( + get_console_info, + INVALID_HANDLE_VALUE, + IS_WINDOWS, +) from tests.test_terminal_io import MockKernel32 diff --git a/tests/test_terminal_io/test_set_terminal_title.py b/tests/test_terminal_io/test_set_terminal_title.py index 38980be..297325b 100644 --- a/tests/test_terminal_io/test_set_terminal_title.py +++ b/tests/test_terminal_io/test_set_terminal_title.py @@ -1,4 +1,3 @@ -# coding: utf-8 """Test function in module.""" import sys @@ -7,7 +6,7 @@ from textwrap import dedent import py import pytest -from terminaltables.terminal_io import IS_WINDOWS, set_terminal_title +from terminaltables3.terminal_io import IS_WINDOWS, set_terminal_title from tests import PROJECT_ROOT from tests.screenshot import RunNewConsole, screenshot_until_match @@ -15,9 +14,10 @@ from tests.test_terminal_io import MockKernel32 HERE = py.path.local(__file__).dirpath() + @pytest.mark.skip("Fails on windows, I didn't touch it") -@pytest.mark.parametrize('is_windows', [False, True]) -@pytest.mark.parametrize('mode', ['ascii', 'unicode', 'bytes']) +@pytest.mark.parametrize("is_windows", [False, True]) +@pytest.mark.parametrize("mode", ["ascii", "unicode", "bytes"]) def test(monkeypatch, is_windows, mode): """Test function. @@ -25,16 +25,16 @@ def test(monkeypatch, is_windows, mode): :param bool is_windows: Monkeypatch terminal_io.IS_WINDOWS :param str mode: Scenario to test for. """ - monkeypatch.setattr('terminaltables.terminal_io.IS_WINDOWS', is_windows) + monkeypatch.setattr("terminaltables3.terminal_io.IS_WINDOWS", is_windows) kernel32 = MockKernel32() # Title. - if mode == 'ascii': - title = 'Testing terminaltables.' - elif mode == 'unicode': - title = u'Testing terminaltables with unicode: 世界你好蓝色' + if mode == "ascii": + title = "Testing terminaltables3." + elif mode == "unicode": + title = "Testing terminaltables3 with unicode: 世界你好蓝色" else: - title = b'Testing terminaltables with bytes.' + title = b"Testing terminaltables3 with bytes." # Run. assert set_terminal_title(title, kernel32) @@ -42,10 +42,10 @@ def test(monkeypatch, is_windows, mode): return # Verify. - if mode == 'ascii': + if mode == "ascii": assert kernel32.setConsoleTitleA_called assert not kernel32.setConsoleTitleW_called - elif mode == 'unicode': + elif mode == "unicode": assert not kernel32.setConsoleTitleA_called assert kernel32.setConsoleTitleW_called else: @@ -54,35 +54,36 @@ def test(monkeypatch, is_windows, mode): @pytest.mark.skipif(str(not IS_WINDOWS)) -@pytest.mark.parametrize('mode', ['ascii', 'unicode', 'bytes']) -@pytest.mark.skip # https://github.com/Robpol86/terminaltables/issues/44 +@pytest.mark.parametrize("mode", ["ascii", "unicode", "bytes"]) +@pytest.mark.skip # https://github.com/Robpol86/terminaltables3/issues/44 def test_windows_screenshot(tmpdir, mode): """Test function on Windows in a new console window. Take a screenshot to verify it works. :param tmpdir: pytest fixture. :param str mode: Scenario to test for. """ - script = tmpdir.join('script.py') + script = tmpdir.join("script.py") command = [sys.executable, str(script)] - change_title = tmpdir.join('change_title') - screenshot = PROJECT_ROOT.join('test_terminal_io_{0}.png'.format(mode)) + change_title = tmpdir.join("change_title") + screenshot = PROJECT_ROOT.join(f"test_terminal_io_{mode}.png") if screenshot.check(): screenshot.remove() # Determine title. - if mode == 'ascii': + if mode == "ascii": title = "'test ASCII test'" - elif mode == 'unicode': - title = u"u'test 世界你好蓝色 test'" + elif mode == "unicode": + title = "u'test 世界你好蓝色 test'" else: title = "b'test ASCII test'" # Generate script. - script_template = dedent(u"""\ + script_template = dedent( + """\ # coding: utf-8 from __future__ import print_function import os, time - from terminaltables.terminal_io import set_terminal_title + from terminaltables3.terminal_io import set_terminal_title stop_after = time.time() + 20 print('Waiting for FindWindowA() in RunNewConsole.__enter__()...') @@ -93,15 +94,18 @@ def test_windows_screenshot(tmpdir, mode): print('Waiting for screenshot_until_match()...') while not os.path.exists(r'{screenshot}') and time.time() < stop_after: time.sleep(0.5) - """) - script_contents = script_template.format(change_title=str(change_title), title=title, screenshot=str(screenshot)) - script.write(script_contents.encode('utf-8'), mode='wb') + """ + ) + script_contents = script_template.format( + change_title=str(change_title), title=title, screenshot=str(screenshot) + ) + script.write(script_contents.encode("utf-8"), mode="wb") # Setup expected. - if mode == 'unicode': - sub_images = [str(p) for p in HERE.listdir('sub_title_cjk_*.bmp')] + if mode == "unicode": + sub_images = [str(p) for p in HERE.listdir("sub_title_cjk_*.bmp")] else: - sub_images = [str(p) for p in HERE.listdir('sub_title_ascii_*.bmp')] + sub_images = [str(p) for p in HERE.listdir("sub_title_ascii_*.bmp")] assert sub_images # Run. diff --git a/tests/test_terminal_io/test_terminal_size.py b/tests/test_terminal_io/test_terminal_size.py index ba14d18..69945a3 100644 --- a/tests/test_terminal_io/test_terminal_size.py +++ b/tests/test_terminal_io/test_terminal_size.py @@ -1,15 +1,20 @@ -# coding: utf-8 """Test function in module.""" import pytest -from terminaltables.terminal_io import DEFAULT_HEIGHT, DEFAULT_WIDTH, INVALID_HANDLE_VALUE, IS_WINDOWS, terminal_size +from terminaltables3.terminal_io import ( + DEFAULT_HEIGHT, + DEFAULT_WIDTH, + INVALID_HANDLE_VALUE, + IS_WINDOWS, + terminal_size, +) from tests.test_terminal_io import MockKernel32 -@pytest.mark.parametrize('stderr', [1, INVALID_HANDLE_VALUE]) -@pytest.mark.parametrize('stdout', [2, INVALID_HANDLE_VALUE]) +@pytest.mark.parametrize("stderr", [1, INVALID_HANDLE_VALUE]) +@pytest.mark.parametrize("stdout", [2, INVALID_HANDLE_VALUE]) def test_windows(monkeypatch, stderr, stdout): """Test function with IS_WINDOWS=True. @@ -17,7 +22,7 @@ def test_windows(monkeypatch, stderr, stdout): :param int stderr: Mock handle value. :param int stdout: Mock handle value. """ - monkeypatch.setattr('terminaltables.terminal_io.IS_WINDOWS', True) + monkeypatch.setattr("terminaltables3.terminal_io.IS_WINDOWS", True) kernel32 = MockKernel32(stderr=stderr, stdout=stdout) width, height = terminal_size(kernel32) @@ -48,7 +53,7 @@ def test_nix(monkeypatch): assert height == DEFAULT_HEIGHT # Test mocked. - monkeypatch.setattr('fcntl.ioctl', lambda *_: b'\x1d\x00w\x00\xca\x02\x96\x01') + monkeypatch.setattr("fcntl.ioctl", lambda *_: b"\x1d\x00w\x00\xca\x02\x96\x01") width, height = terminal_size() assert width == 119 assert height == 29 diff --git a/tests/test_width_and_alignment/__init__.py b/tests/test_width_and_alignment/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/test_width_and_alignment/__init__.py diff --git a/tests/test_width_and_alignment/test_align_and_pad_cell.py b/tests/test_width_and_alignment/test_align_and_pad_cell.py index e0a928e..b0c441b 100644 --- a/tests/test_width_and_alignment/test_align_and_pad_cell.py +++ b/tests/test_width_and_alignment/test_align_and_pad_cell.py @@ -1,4 +1,3 @@ -# coding: utf-8 """Test function in module.""" import pytest @@ -6,75 +5,74 @@ from colorama import Fore from colorclass import Color from termcolor import colored -from terminaltables.width_and_alignment import align_and_pad_cell - - -@pytest.mark.parametrize('string,align,width,expected', [ - ('test', '', 4, ['test']), - (123, '', 3, ['123']), - (0.9, '', 3, ['0.9']), - (None, '', 4, ['None']), - (True, '', 4, ['True']), - (False, '', 5, ['False']), - (Color('{blue}Test{/blue}'), '', 4, ['\x1b[34mTest\x1b[39m']), - (Fore.BLUE + 'Test' + Fore.RESET, '', 4, ['\x1b[34mTest\x1b[39m']), - (colored('Test', 'blue'), '', 4, ['\x1b[34mTest\x1b[0m']), - ('蓝色', '', 4, ['蓝色']), - (u'שלום', '', 4, [u'\u05e9\u05dc\u05d5\u05dd']), - (u'معرب', '', 4, [u'\u0645\u0639\u0631\u0628']), - - ('test', '', 5, ['test ']), - (123, '', 4, ['123 ']), - (0.9, '', 4, ['0.9 ']), - (None, '', 5, ['None ']), - (True, '', 5, ['True ']), - (False, '', 6, ['False ']), - (Color('{blue}Test{/blue}'), '', 5, ['\x1b[34mTest\x1b[39m ']), - (Fore.BLUE + 'Test' + Fore.RESET, '', 5, ['\x1b[34mTest\x1b[39m ']), - (colored('Test', 'blue'), '', 5, ['\x1b[34mTest\x1b[0m ']), - ('蓝色', '', 5, ['蓝色 ']), - (u'שלום', '', 5, [u'\u05e9\u05dc\u05d5\u05dd ']), - (u'معرب', '', 5, [u'\u0645\u0639\u0631\u0628 ']), - - ('test', 'left', 5, ['test ']), - (123, 'left', 4, ['123 ']), - (0.9, 'left', 4, ['0.9 ']), - (None, 'left', 5, ['None ']), - (True, 'left', 5, ['True ']), - (False, 'left', 6, ['False ']), - (Color('{blue}Test{/blue}'), 'left', 5, ['\x1b[34mTest\x1b[39m ']), - (Fore.BLUE + 'Test' + Fore.RESET, 'left', 5, ['\x1b[34mTest\x1b[39m ']), - (colored('Test', 'blue'), 'left', 5, ['\x1b[34mTest\x1b[0m ']), - ('蓝色', 'left', 5, ['蓝色 ']), - (u'שלום', 'left', 5, [u'\u05e9\u05dc\u05d5\u05dd ']), - (u'معرب', 'left', 5, [u'\u0645\u0639\u0631\u0628 ']), - - ('test', 'right', 5, [' test']), - (123, 'right', 4, [' 123']), - (0.9, 'right', 4, [' 0.9']), - (None, 'right', 5, [' None']), - (True, 'right', 5, [' True']), - (False, 'right', 6, [' False']), - (Color('{blue}Test{/blue}'), 'right', 5, [' \x1b[34mTest\x1b[39m']), - (Fore.BLUE + 'Test' + Fore.RESET, 'right', 5, [' \x1b[34mTest\x1b[39m']), - (colored('Test', 'blue'), 'right', 5, [' \x1b[34mTest\x1b[0m']), - ('蓝色', 'right', 5, [' 蓝色']), - (u'שלום', 'right', 5, [u' \u05e9\u05dc\u05d5\u05dd']), - (u'معرب', 'right', 5, [u' \u0645\u0639\u0631\u0628']), - - ('test', 'center', 6, [' test ']), - (123, 'center', 5, [' 123 ']), - (0.9, 'center', 5, [' 0.9 ']), - (None, 'center', 6, [' None ']), - (True, 'center', 6, [' True ']), - (False, 'center', 7, [' False ']), - (Color('{blue}Test{/blue}'), 'center', 6, [' \x1b[34mTest\x1b[39m ']), - (Fore.BLUE + 'Test' + Fore.RESET, 'center', 6, [' \x1b[34mTest\x1b[39m ']), - (colored('Test', 'blue'), 'center', 6, [' \x1b[34mTest\x1b[0m ']), - ('蓝色', 'center', 6, [' 蓝色 ']), - (u'שלום', 'center', 6, [u' \u05e9\u05dc\u05d5\u05dd ']), - (u'معرب', 'center', 6, [u' \u0645\u0639\u0631\u0628 ']), -]) +from terminaltables3.width_and_alignment import align_and_pad_cell + + +@pytest.mark.parametrize( + "string,align,width,expected", + [ + ("test", "", 4, ["test"]), + (123, "", 3, ["123"]), + (0.9, "", 3, ["0.9"]), + (None, "", 4, ["None"]), + (True, "", 4, ["True"]), + (False, "", 5, ["False"]), + (Color("{blue}Test{/blue}"), "", 4, ["\x1b[34mTest\x1b[39m"]), + (Fore.BLUE + "Test" + Fore.RESET, "", 4, ["\x1b[34mTest\x1b[39m"]), + (colored("Test", "blue"), "", 4, ["\x1b[34mTest\x1b[0m"]), + ("蓝色", "", 4, ["蓝色"]), + ("שלום", "", 4, ["\u05e9\u05dc\u05d5\u05dd"]), + ("معرب", "", 4, ["\u0645\u0639\u0631\u0628"]), + ("test", "", 5, ["test "]), + (123, "", 4, ["123 "]), + (0.9, "", 4, ["0.9 "]), + (None, "", 5, ["None "]), + (True, "", 5, ["True "]), + (False, "", 6, ["False "]), + (Color("{blue}Test{/blue}"), "", 5, ["\x1b[34mTest\x1b[39m "]), + (Fore.BLUE + "Test" + Fore.RESET, "", 5, ["\x1b[34mTest\x1b[39m "]), + (colored("Test", "blue"), "", 5, ["\x1b[34mTest\x1b[0m "]), + ("蓝色", "", 5, ["蓝色 "]), + ("שלום", "", 5, ["\u05e9\u05dc\u05d5\u05dd "]), + ("معرب", "", 5, ["\u0645\u0639\u0631\u0628 "]), + ("test", "left", 5, ["test "]), + (123, "left", 4, ["123 "]), + (0.9, "left", 4, ["0.9 "]), + (None, "left", 5, ["None "]), + (True, "left", 5, ["True "]), + (False, "left", 6, ["False "]), + (Color("{blue}Test{/blue}"), "left", 5, ["\x1b[34mTest\x1b[39m "]), + (Fore.BLUE + "Test" + Fore.RESET, "left", 5, ["\x1b[34mTest\x1b[39m "]), + (colored("Test", "blue"), "left", 5, ["\x1b[34mTest\x1b[0m "]), + ("蓝色", "left", 5, ["蓝色 "]), + ("שלום", "left", 5, ["\u05e9\u05dc\u05d5\u05dd "]), + ("معرب", "left", 5, ["\u0645\u0639\u0631\u0628 "]), + ("test", "right", 5, [" test"]), + (123, "right", 4, [" 123"]), + (0.9, "right", 4, [" 0.9"]), + (None, "right", 5, [" None"]), + (True, "right", 5, [" True"]), + (False, "right", 6, [" False"]), + (Color("{blue}Test{/blue}"), "right", 5, [" \x1b[34mTest\x1b[39m"]), + (Fore.BLUE + "Test" + Fore.RESET, "right", 5, [" \x1b[34mTest\x1b[39m"]), + (colored("Test", "blue"), "right", 5, [" \x1b[34mTest\x1b[0m"]), + ("蓝色", "right", 5, [" 蓝色"]), + ("שלום", "right", 5, [" \u05e9\u05dc\u05d5\u05dd"]), + ("معرب", "right", 5, [" \u0645\u0639\u0631\u0628"]), + ("test", "center", 6, [" test "]), + (123, "center", 5, [" 123 "]), + (0.9, "center", 5, [" 0.9 "]), + (None, "center", 6, [" None "]), + (True, "center", 6, [" True "]), + (False, "center", 7, [" False "]), + (Color("{blue}Test{/blue}"), "center", 6, [" \x1b[34mTest\x1b[39m "]), + (Fore.BLUE + "Test" + Fore.RESET, "center", 6, [" \x1b[34mTest\x1b[39m "]), + (colored("Test", "blue"), "center", 6, [" \x1b[34mTest\x1b[0m "]), + ("蓝色", "center", 6, [" 蓝色 "]), + ("שלום", "center", 6, [" \u05e9\u05dc\u05d5\u05dd "]), + ("معرب", "center", 6, [" \u0645\u0639\u0631\u0628 "]), + ], +) def test_width(string, align, width, expected): """Test width and horizontal alignment. @@ -87,47 +85,61 @@ def test_width(string, align, width, expected): assert actual == expected -@pytest.mark.parametrize('string,align,height,expected', [ - ('test', '', 1, ['test']), - (Color('{blue}Test{/blue}'), '', 1, ['\x1b[34mTest\x1b[39m']), - (Fore.BLUE + 'Test' + Fore.RESET, '', 1, ['\x1b[34mTest\x1b[39m']), - (colored('Test', 'blue'), '', 1, ['\x1b[34mTest\x1b[0m']), - ('蓝色', '', 1, ['蓝色']), - (u'שלום', '', 1, [u'\u05e9\u05dc\u05d5\u05dd']), - (u'معرب', '', 1, [u'\u0645\u0639\u0631\u0628']), - - ('test', '', 2, ['test', ' ']), - (Color('{blue}Test{/blue}'), '', 2, ['\x1b[34mTest\x1b[39m', ' ']), - (Fore.BLUE + 'Test' + Fore.RESET, '', 2, ['\x1b[34mTest\x1b[39m', ' ']), - (colored('Test', 'blue'), '', 2, ['\x1b[34mTest\x1b[0m', ' ']), - ('蓝色', '', 2, ['蓝色', ' ']), - (u'שלום', '', 2, [u'\u05e9\u05dc\u05d5\u05dd', ' ']), - (u'معرب', '', 2, [u'\u0645\u0639\u0631\u0628', ' ']), - - ('test', 'top', 2, ['test', ' ']), - (Color('{blue}Test{/blue}'), 'top', 2, ['\x1b[34mTest\x1b[39m', ' ']), - (Fore.BLUE + 'Test' + Fore.RESET, 'top', 2, ['\x1b[34mTest\x1b[39m', ' ']), - (colored('Test', 'blue'), 'top', 2, ['\x1b[34mTest\x1b[0m', ' ']), - ('蓝色', 'top', 2, ['蓝色', ' ']), - (u'שלום', 'top', 2, [u'\u05e9\u05dc\u05d5\u05dd', ' ']), - (u'معرب', 'top', 2, [u'\u0645\u0639\u0631\u0628', ' ']), - - ('test', 'bottom', 2, [' ', 'test']), - (Color('{blue}Test{/blue}'), 'bottom', 2, [' ', '\x1b[34mTest\x1b[39m']), - (Fore.BLUE + 'Test' + Fore.RESET, 'bottom', 2, [' ', '\x1b[34mTest\x1b[39m']), - (colored('Test', 'blue'), 'bottom', 2, [' ', '\x1b[34mTest\x1b[0m']), - ('蓝色', 'bottom', 2, [' ', '蓝色']), - (u'שלום', 'bottom', 2, [' ', u'\u05e9\u05dc\u05d5\u05dd']), - (u'معرب', 'bottom', 2, [' ', u'\u0645\u0639\u0631\u0628']), - - ('test', 'middle', 3, [' ', 'test', ' ']), - (Color('{blue}Test{/blue}'), 'middle', 3, [' ', '\x1b[34mTest\x1b[39m', ' ']), - (Fore.BLUE + 'Test' + Fore.RESET, 'middle', 3, [' ', '\x1b[34mTest\x1b[39m', ' ']), - (colored('Test', 'blue'), 'middle', 3, [' ', '\x1b[34mTest\x1b[0m', ' ']), - ('蓝色', 'middle', 3, [' ', '蓝色', ' ']), - (u'שלום', 'middle', 3, [' ', u'\u05e9\u05dc\u05d5\u05dd', ' ']), - (u'معرب', 'middle', 3, [' ', u'\u0645\u0639\u0631\u0628', ' ']), -]) +@pytest.mark.parametrize( + "string,align,height,expected", + [ + ("test", "", 1, ["test"]), + (Color("{blue}Test{/blue}"), "", 1, ["\x1b[34mTest\x1b[39m"]), + (Fore.BLUE + "Test" + Fore.RESET, "", 1, ["\x1b[34mTest\x1b[39m"]), + (colored("Test", "blue"), "", 1, ["\x1b[34mTest\x1b[0m"]), + ("蓝色", "", 1, ["蓝色"]), + ("שלום", "", 1, ["\u05e9\u05dc\u05d5\u05dd"]), + ("معرب", "", 1, ["\u0645\u0639\u0631\u0628"]), + ("test", "", 2, ["test", " "]), + (Color("{blue}Test{/blue}"), "", 2, ["\x1b[34mTest\x1b[39m", " "]), + (Fore.BLUE + "Test" + Fore.RESET, "", 2, ["\x1b[34mTest\x1b[39m", " "]), + (colored("Test", "blue"), "", 2, ["\x1b[34mTest\x1b[0m", " "]), + ("蓝色", "", 2, ["蓝色", " "]), + ("שלום", "", 2, ["\u05e9\u05dc\u05d5\u05dd", " "]), + ("معرب", "", 2, ["\u0645\u0639\u0631\u0628", " "]), + ("test", "top", 2, ["test", " "]), + (Color("{blue}Test{/blue}"), "top", 2, ["\x1b[34mTest\x1b[39m", " "]), + (Fore.BLUE + "Test" + Fore.RESET, "top", 2, ["\x1b[34mTest\x1b[39m", " "]), + (colored("Test", "blue"), "top", 2, ["\x1b[34mTest\x1b[0m", " "]), + ("蓝色", "top", 2, ["蓝色", " "]), + ("שלום", "top", 2, ["\u05e9\u05dc\u05d5\u05dd", " "]), + ("معرب", "top", 2, ["\u0645\u0639\u0631\u0628", " "]), + ("test", "bottom", 2, [" ", "test"]), + (Color("{blue}Test{/blue}"), "bottom", 2, [" ", "\x1b[34mTest\x1b[39m"]), + ( + Fore.BLUE + "Test" + Fore.RESET, + "bottom", + 2, + [" ", "\x1b[34mTest\x1b[39m"], + ), + (colored("Test", "blue"), "bottom", 2, [" ", "\x1b[34mTest\x1b[0m"]), + ("蓝色", "bottom", 2, [" ", "蓝色"]), + ("שלום", "bottom", 2, [" ", "\u05e9\u05dc\u05d5\u05dd"]), + ("معرب", "bottom", 2, [" ", "\u0645\u0639\u0631\u0628"]), + ("test", "middle", 3, [" ", "test", " "]), + ( + Color("{blue}Test{/blue}"), + "middle", + 3, + [" ", "\x1b[34mTest\x1b[39m", " "], + ), + ( + Fore.BLUE + "Test" + Fore.RESET, + "middle", + 3, + [" ", "\x1b[34mTest\x1b[39m", " "], + ), + (colored("Test", "blue"), "middle", 3, [" ", "\x1b[34mTest\x1b[0m", " "]), + ("蓝色", "middle", 3, [" ", "蓝色", " "]), + ("שלום", "middle", 3, [" ", "\u05e9\u05dc\u05d5\u05dd", " "]), + ("معرب", "middle", 3, [" ", "\u0645\u0639\u0631\u0628", " "]), + ], +) def test_height(string, align, height, expected): """Test height and vertical alignment. @@ -140,57 +152,68 @@ def test_height(string, align, height, expected): assert actual == expected -@pytest.mark.parametrize('string,align,expected', [ - ('', '', ['.......', '.......', '.......', '.......', '.......']), - ('\n', '', ['.......', '.......', '.......', '.......', '.......']), - ('a\nb\nc', '', ['.......', '.a.....', '.b.....', '.c.....', '.......']), - ('test', '', ['.......', '.test..', '.......', '.......', '.......']), - - ('', 'left', ['.......', '.......', '.......', '.......', '.......']), - ('\n', 'left', ['.......', '.......', '.......', '.......', '.......']), - ('a\nb\nc', 'left', ['.......', '.a.....', '.b.....', '.c.....', '.......']), - ('test', 'left', ['.......', '.test..', '.......', '.......', '.......']), - - ('', 'right', ['.......', '.......', '.......', '.......', '.......']), - ('\n', 'right', ['.......', '.......', '.......', '.......', '.......']), - ('a\nb\nc', 'right', ['.......', '.....a.', '.....b.', '.....c.', '.......']), - ('test', 'right', ['.......', '..test.', '.......', '.......', '.......']), - - ('', 'center', ['.......', '.......', '.......', '.......', '.......']), - ('\n', 'center', ['.......', '.......', '.......', '.......', '.......']), - ('a\nb\nc', 'center', ['.......', '...a...', '...b...', '...c...', '.......']), - ('test', 'center', ['.......', '..test.', '.......', '.......', '.......']), - - ('', 'top', ['.......', '.......', '.......', '.......', '.......']), - ('\n', 'top', ['.......', '.......', '.......', '.......', '.......']), - ('a\nb\nc', 'top', ['.......', '.a.....', '.b.....', '.c.....', '.......']), - ('test', 'top', ['.......', '.test..', '.......', '.......', '.......']), - - ('', 'bottom', ['.......', '.......', '.......', '.......', '.......']), - ('\n', 'bottom', ['.......', '.......', '.......', '.......', '.......']), - ('a\nb\nc', 'bottom', ['.......', '.a.....', '.b.....', '.c.....', '.......']), - ('test', 'bottom', ['.......', '.......', '.......', '.test..', '.......']), - - ('', 'middle', ['.......', '.......', '.......', '.......', '.......']), - ('\n', 'middle', ['.......', '.......', '.......', '.......', '.......']), - ('a\nb\nc', 'middle', ['.......', '.a.....', '.b.....', '.c.....', '.......']), - ('test', 'middle', ['.......', '.......', '.test..', '.......', '.......']), - - ( - u'蓝色\nשלום\nمعرب', - '', - ['.......', u'.蓝色..', u'.\u05e9\u05dc\u05d5\u05dd..', u'.\u0645\u0639\u0631\u0628..', '.......'] - ), - - ( - '\n'.join((Color('{blue}Test{/blue}'), Fore.BLUE + 'Test' + Fore.RESET, colored('Test', 'blue'))), - '', - ['.......', '.\x1b[34mTest\x1b[39m..', '.\x1b[34mTest\x1b[39m..', '.\x1b[34mTest\x1b[0m..', '.......'] - ), - - # (Color('{blue}A\nB{/blue}'), '', '.......\n.\x1b[34mA\x1b[39m.....\n.\x1b[34mB\x1b[39m.....\n.......\n.......'), - -]) +@pytest.mark.parametrize( + "string,align,expected", + [ + ("", "", [".......", ".......", ".......", ".......", "......."]), + ("\n", "", [".......", ".......", ".......", ".......", "......."]), + ("a\nb\nc", "", [".......", ".a.....", ".b.....", ".c.....", "......."]), + ("test", "", [".......", ".test..", ".......", ".......", "......."]), + ("", "left", [".......", ".......", ".......", ".......", "......."]), + ("\n", "left", [".......", ".......", ".......", ".......", "......."]), + ("a\nb\nc", "left", [".......", ".a.....", ".b.....", ".c.....", "......."]), + ("test", "left", [".......", ".test..", ".......", ".......", "......."]), + ("", "right", [".......", ".......", ".......", ".......", "......."]), + ("\n", "right", [".......", ".......", ".......", ".......", "......."]), + ("a\nb\nc", "right", [".......", ".....a.", ".....b.", ".....c.", "......."]), + ("test", "right", [".......", "..test.", ".......", ".......", "......."]), + ("", "center", [".......", ".......", ".......", ".......", "......."]), + ("\n", "center", [".......", ".......", ".......", ".......", "......."]), + ("a\nb\nc", "center", [".......", "...a...", "...b...", "...c...", "......."]), + ("test", "center", [".......", "..test.", ".......", ".......", "......."]), + ("", "top", [".......", ".......", ".......", ".......", "......."]), + ("\n", "top", [".......", ".......", ".......", ".......", "......."]), + ("a\nb\nc", "top", [".......", ".a.....", ".b.....", ".c.....", "......."]), + ("test", "top", [".......", ".test..", ".......", ".......", "......."]), + ("", "bottom", [".......", ".......", ".......", ".......", "......."]), + ("\n", "bottom", [".......", ".......", ".......", ".......", "......."]), + ("a\nb\nc", "bottom", [".......", ".a.....", ".b.....", ".c.....", "......."]), + ("test", "bottom", [".......", ".......", ".......", ".test..", "......."]), + ("", "middle", [".......", ".......", ".......", ".......", "......."]), + ("\n", "middle", [".......", ".......", ".......", ".......", "......."]), + ("a\nb\nc", "middle", [".......", ".a.....", ".b.....", ".c.....", "......."]), + ("test", "middle", [".......", ".......", ".test..", ".......", "......."]), + ( + "蓝色\nשלום\nمعرب", + "", + [ + ".......", + ".蓝色..", + ".\u05e9\u05dc\u05d5\u05dd..", + ".\u0645\u0639\u0631\u0628..", + ".......", + ], + ), + ( + "\n".join( + ( + Color("{blue}Test{/blue}"), + Fore.BLUE + "Test" + Fore.RESET, + colored("Test", "blue"), + ) + ), + "", + [ + ".......", + ".\x1b[34mTest\x1b[39m..", + ".\x1b[34mTest\x1b[39m..", + ".\x1b[34mTest\x1b[0m..", + ".......", + ], + ), + # (Color('{blue}A\nB{/blue}'), '', '.......\n.\x1b[34mA\x1b[39m.....\n.\x1b[34mB\x1b[39m.....\n.......\n.......'), + ], +) def test_odd_width_height_pad_space(string, align, expected): """Test odd number width, height, padding, and dots for whitespaces. @@ -198,5 +221,5 @@ def test_odd_width_height_pad_space(string, align, expected): :param str align: Alignment in any dimension but one at a time. :param list expected: Expected output string. """ - actual = align_and_pad_cell(string, (align,), (5, 3), (1, 1, 1, 1), '.') + actual = align_and_pad_cell(string, (align,), (5, 3), (1, 1, 1, 1), ".") assert actual == expected diff --git a/tests/test_width_and_alignment/test_column_max_width.py b/tests/test_width_and_alignment/test_column_max_width.py index 696c9bf..47a9c30 100644 --- a/tests/test_width_and_alignment/test_column_max_width.py +++ b/tests/test_width_and_alignment/test_column_max_width.py @@ -2,7 +2,7 @@ import pytest -from terminaltables.width_and_alignment import column_max_width, max_dimensions +from terminaltables3.width_and_alignment import column_max_width, max_dimensions @pytest.fixture(autouse=True) @@ -11,27 +11,35 @@ def patch(monkeypatch): :param monkeypatch: pytest fixture. """ - monkeypatch.setattr('terminaltables.width_and_alignment.terminal_size', lambda: (79, 24)) + monkeypatch.setattr( + "terminaltables3.width_and_alignment.terminal_size", lambda: (79, 24) + ) def test_empty(): """Test with zero-length cells.""" - assert column_max_width(max_dimensions([['']])[0], 0, 0, 0, 0) == 79 - assert column_max_width(max_dimensions([['', '', '']])[0], 0, 0, 0, 0) == 79 - assert column_max_width(max_dimensions([['', '', ''], ['', '', '']])[0], 0, 0, 0, 0) == 79 + assert column_max_width(max_dimensions([[""]])[0], 0, 0, 0, 0) == 79 + assert column_max_width(max_dimensions([["", "", ""]])[0], 0, 0, 0, 0) == 79 + assert ( + column_max_width(max_dimensions([["", "", ""], ["", "", ""]])[0], 0, 0, 0, 0) + == 79 + ) - assert column_max_width(max_dimensions([['']])[0], 0, 2, 1, 2) == 75 - assert column_max_width(max_dimensions([['', '', '']])[0], 0, 2, 1, 2) == 69 - assert column_max_width(max_dimensions([['', '', ''], ['', '', '']])[0], 0, 2, 1, 2) == 69 + assert column_max_width(max_dimensions([[""]])[0], 0, 2, 1, 2) == 75 + assert column_max_width(max_dimensions([["", "", ""]])[0], 0, 2, 1, 2) == 69 + assert ( + column_max_width(max_dimensions([["", "", ""], ["", "", ""]])[0], 0, 2, 1, 2) + == 69 + ) def test_single_line(): """Test with single-line cells.""" table_data = [ - ['Name', 'Color', 'Type'], - ['Avocado', 'green', 'nut'], - ['Tomato', 'red', 'fruit'], - ['Lettuce', 'green', 'vegetable'], + ["Name", "Color", "Type"], + ["Avocado", "green", "nut"], + ["Tomato", "red", "fruit"], + ["Lettuce", "green", "vegetable"], ] inner_widths = max_dimensions(table_data)[0] @@ -72,11 +80,11 @@ def test_single_line(): assert column_max_width(inner_widths, 2, outer, inner, padding) == 48 table_data = [ - ['Name', 'Color', 'Type'], - ['Avocado', 'green', 'nut'], - ['Tomato', 'red', 'fruit'], - ['Lettuce', 'green', 'vegetable'], - ['Watermelon', 'green', 'fruit'], + ["Name", "Color", "Type"], + ["Avocado", "green", "nut"], + ["Tomato", "red", "fruit"], + ["Lettuce", "green", "vegetable"], + ["Watermelon", "green", "fruit"], ] inner_widths = max_dimensions(table_data)[0] outer, inner, padding = 2, 1, 2 @@ -91,10 +99,15 @@ def test_multi_line(monkeypatch): :param monkeypatch: pytest fixture. """ table_data = [ - ['Show', 'Characters'], - ['Rugrats', ('Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\n' - 'Susie Carmichael, Dil Pickles, Kimi Finster, Spike')], - ['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] + ["Show", "Characters"], + [ + "Rugrats", + ( + "Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\n" + "Susie Carmichael, Dil Pickles, Kimi Finster, Spike" + ), + ], + ["South Park", "Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick"], ] inner_widths = max_dimensions(table_data)[0] outer, inner, padding = 2, 1, 2 @@ -102,6 +115,8 @@ def test_multi_line(monkeypatch): assert column_max_width(inner_widths, 0, outer, inner, padding) == -11 assert column_max_width(inner_widths, 1, outer, inner, padding) == 62 - monkeypatch.setattr('terminaltables.width_and_alignment.terminal_size', lambda: (100, 24)) + monkeypatch.setattr( + "terminaltables3.width_and_alignment.terminal_size", lambda: (100, 24) + ) assert column_max_width(inner_widths, 0, outer, inner, padding) == 10 assert column_max_width(inner_widths, 1, outer, inner, padding) == 83 diff --git a/tests/test_width_and_alignment/test_max_dimensions.py b/tests/test_width_and_alignment/test_max_dimensions.py index fc97883..5dd2629 100644 --- a/tests/test_width_and_alignment/test_max_dimensions.py +++ b/tests/test_width_and_alignment/test_max_dimensions.py @@ -1,4 +1,3 @@ -# coding: utf-8 """Test function in module.""" import pytest @@ -6,18 +5,20 @@ from colorama import Fore from colorclass import Color from termcolor import colored -from terminaltables.width_and_alignment import max_dimensions +from terminaltables3.width_and_alignment import max_dimensions -@pytest.mark.parametrize('table_data,expected_w,expected_h', [ - ([[]], [], [0]), - ([['']], [0], [0]), - ([['', '']], [0, 0], [0]), - - ([[], []], [], [0, 0]), - ([[''], ['']], [0], [0, 0]), - ([['', ''], ['', '']], [0, 0], [0, 0]), -]) +@pytest.mark.parametrize( + "table_data,expected_w,expected_h", + [ + ([[]], [], [0]), + ([[""]], [0], [0]), + ([["", ""]], [0, 0], [0]), + ([[], []], [], [0, 0]), + ([[""], [""]], [0], [0, 0]), + ([["", ""], ["", ""]], [0, 0], [0, 0]), + ], +) def test_zero_length(table_data, expected_w, expected_h): """Test zero-length or empty tables. @@ -32,40 +33,60 @@ def test_zero_length(table_data, expected_w, expected_h): def test_single_line(): """Test widths.""" table_data = [ - ['Name', 'Color', 'Type'], - ['Avocado', 'green', 'nut'], - ['Tomato', 'red', 'fruit'], - ['Lettuce', 'green', 'vegetable'], + ["Name", "Color", "Type"], + ["Avocado", "green", "nut"], + ["Tomato", "red", "fruit"], + ["Lettuce", "green", "vegetable"], ] - assert max_dimensions(table_data, 1, 1) == ([7, 5, 9], [1, 1, 1, 1], [9, 7, 11], [1, 1, 1, 1]) - - table_data.append(['Watermelon', 'green', 'fruit']) - assert max_dimensions(table_data, 2, 2) == ([10, 5, 9], [1, 1, 1, 1, 1], [14, 9, 13], [1, 1, 1, 1, 1]) + assert max_dimensions(table_data, 1, 1) == ( + [7, 5, 9], + [1, 1, 1, 1], + [9, 7, 11], + [1, 1, 1, 1], + ) + + table_data.append(["Watermelon", "green", "fruit"]) + assert max_dimensions(table_data, 2, 2) == ( + [10, 5, 9], + [1, 1, 1, 1, 1], + [14, 9, 13], + [1, 1, 1, 1, 1], + ) def test_multi_line(): """Test heights.""" table_data = [ - ['One\nTwo', 'Buckle\nMy\nShoe'], + ["One\nTwo", "Buckle\nMy\nShoe"], ] assert max_dimensions(table_data, 0, 0, 1, 1) == ([3, 6], [3], [3, 6], [5]) table_data = [ - ['Show', 'Characters'], - ['Rugrats', ('Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\n' - 'Susie Carmichael, Dil Pickles, Kimi Finster, Spike')], - ['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] + ["Show", "Characters"], + [ + "Rugrats", + ( + "Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\n" + "Susie Carmichael, Dil Pickles, Kimi Finster, Spike" + ), + ], + ["South Park", "Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick"], ] - assert max_dimensions(table_data, 0, 0, 2, 2) == ([10, 83], [1, 2, 1], [10, 83], [5, 6, 5]) + assert max_dimensions(table_data, 0, 0, 2, 2) == ( + [10, 83], + [1, 2, 1], + [10, 83], + [5, 6, 5], + ) def test_trailing_newline(): r"""Test with trailing \n.""" table_data = [ - ['Row One\n<blank>'], - ['<blank>\nRow Two'], - ['Row Three\n'], - ['\nRow Four'], + ["Row One\n<blank>"], + ["<blank>\nRow Two"], + ["Row Three\n"], + ["\nRow Four"], ] assert max_dimensions(table_data) == ([9], [2, 2, 2, 2], [9], [2, 2, 2, 2]) @@ -73,21 +94,21 @@ def test_trailing_newline(): def test_colors_cjk_rtl(): """Test color text, CJK characters, and RTL characters.""" table_data = [ - [Color('{blue}Test{/blue}')], - [Fore.BLUE + 'Test' + Fore.RESET], - [colored('Test', 'blue')], + [Color("{blue}Test{/blue}")], + [Fore.BLUE + "Test" + Fore.RESET], + [colored("Test", "blue")], ] assert max_dimensions(table_data) == ([4], [1, 1, 1], [4], [1, 1, 1]) table_data = [ - ['蓝色'], - ['世界你好'], + ["蓝色"], + ["世界你好"], ] assert max_dimensions(table_data) == ([8], [1, 1], [8], [1, 1]) table_data = [ - ['שלום'], - ['معرب'], + ["שלום"], + ["معرب"], ] assert max_dimensions(table_data) == ([4], [1, 1], [4], [1, 1]) diff --git a/tests/test_width_and_alignment/test_table_width.py b/tests/test_width_and_alignment/test_table_width.py index 5818789..15bf73d 100644 --- a/tests/test_width_and_alignment/test_table_width.py +++ b/tests/test_width_and_alignment/test_table_width.py @@ -1,26 +1,28 @@ """Test function in module.""" -from terminaltables.width_and_alignment import max_dimensions, table_width +from terminaltables3.width_and_alignment import max_dimensions, table_width def test_empty(): """Test with zero-length cells.""" - assert table_width(max_dimensions([['']])[2], 0, 0) == 0 - assert table_width(max_dimensions([['', '', '']])[2], 0, 0) == 0 - assert table_width(max_dimensions([['', '', ''], ['', '', '']])[2], 0, 0) == 0 + assert table_width(max_dimensions([[""]])[2], 0, 0) == 0 + assert table_width(max_dimensions([["", "", ""]])[2], 0, 0) == 0 + assert table_width(max_dimensions([["", "", ""], ["", "", ""]])[2], 0, 0) == 0 - assert table_width(max_dimensions([['']], 1, 1)[2], 2, 1) == 4 - assert table_width(max_dimensions([['', '', '']], 1, 1)[2], 2, 1) == 10 - assert table_width(max_dimensions([['', '', ''], ['', '', '']], 1, 1)[2], 2, 1) == 10 + assert table_width(max_dimensions([[""]], 1, 1)[2], 2, 1) == 4 + assert table_width(max_dimensions([["", "", ""]], 1, 1)[2], 2, 1) == 10 + assert ( + table_width(max_dimensions([["", "", ""], ["", "", ""]], 1, 1)[2], 2, 1) == 10 + ) def test_single_line(): """Test with single-line cells.""" table_data = [ - ['Name', 'Color', 'Type'], - ['Avocado', 'green', 'nut'], - ['Tomato', 'red', 'fruit'], - ['Lettuce', 'green', 'vegetable'], + ["Name", "Color", "Type"], + ["Avocado", "green", "nut"], + ["Tomato", "red", "fruit"], + ["Lettuce", "green", "vegetable"], ] # '| Lettuce | green | vegetable |' @@ -48,11 +50,11 @@ def test_single_line(): assert table_width(outer_widths, outer, inner) == 40 table_data = [ - ['Name', 'Color', 'Type'], - ['Avocado', 'green', 'nut'], - ['Tomato', 'red', 'fruit'], - ['Lettuce', 'green', 'vegetable'], - ['Watermelon', 'green', 'fruit'], + ["Name", "Color", "Type"], + ["Avocado", "green", "nut"], + ["Tomato", "red", "fruit"], + ["Lettuce", "green", "vegetable"], + ["Watermelon", "green", "fruit"], ] outer, inner, outer_widths = 2, 1, max_dimensions(table_data, 1, 1)[2] assert table_width(outer_widths, outer, inner) == 34 @@ -61,10 +63,15 @@ def test_single_line(): def test_multi_line(): """Test with multi-line cells.""" table_data = [ - ['Show', 'Characters'], - ['Rugrats', ('Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\n' - 'Susie Carmichael, Dil Pickles, Kimi Finster, Spike')], - ['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] + ["Show", "Characters"], + [ + "Rugrats", + ( + "Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\n" + "Susie Carmichael, Dil Pickles, Kimi Finster, Spike" + ), + ], + ["South Park", "Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick"], ] outer, inner, outer_widths = 2, 1, max_dimensions(table_data, 1, 1)[2] assert table_width(outer_widths, outer, inner) == 100 diff --git a/tests/test_width_and_alignment/test_visible_width.py b/tests/test_width_and_alignment/test_visible_width.py index 79cebcb..53e9116 100644 --- a/tests/test_width_and_alignment/test_visible_width.py +++ b/tests/test_width_and_alignment/test_visible_width.py @@ -1,4 +1,3 @@ -# coding: utf-8 """Test function in module.""" import pytest @@ -6,50 +5,49 @@ from colorama import Fore from colorclass import Color from termcolor import colored -from terminaltables.width_and_alignment import visible_width - - -@pytest.mark.parametrize('string,expected', [ - # str - ('hello, world', 12), - ('世界你好', 8), - ('蓝色', 4), - ('שלום', 4), - ('معرب', 4), - ('hello 世界', 10), - - # str+ansi - ('\x1b[34mhello, world\x1b[39m', 12), - ('\x1b[34m世界你好\x1b[39m', 8), - ('\x1b[34m蓝色\x1b[39m', 4), - ('\x1b[34mשלום\x1b[39m', 4), - ('\x1b[34mمعرب\x1b[39m', 4), - ('\x1b[34mhello 世界\x1b[39m', 10), - - # colorclass - (Color(u'{blue}hello, world{/blue}'), 12), - (Color(u'{blue}世界你好{/blue}'), 8), - (Color(u'{blue}蓝色{/blue}'), 4), - (Color(u'{blue}שלום{/blue}'), 4), - (Color(u'{blue}معرب{/blue}'), 4), - (Color(u'{blue}hello 世界{/blue}'), 10), - - # colorama - (Fore.BLUE + 'hello, world' + Fore.RESET, 12), - (Fore.BLUE + '世界你好' + Fore.RESET, 8), - (Fore.BLUE + '蓝色' + Fore.RESET, 4), - (Fore.BLUE + 'שלום' + Fore.RESET, 4), - (Fore.BLUE + 'معرب' + Fore.RESET, 4), - (Fore.BLUE + 'hello 世界' + Fore.RESET, 10), - - # termcolor - (colored('hello, world', 'blue'), 12), - (colored('世界你好', 'blue'), 8), - (colored('蓝色', 'blue'), 4), - (colored('שלום', 'blue'), 4), - (colored('معرب', 'blue'), 4), - (colored('hello 世界', 'blue'), 10), -]) +from terminaltables3.width_and_alignment import visible_width + + +@pytest.mark.parametrize( + "string,expected", + [ + # str + ("hello, world", 12), + ("世界你好", 8), + ("蓝色", 4), + ("שלום", 4), + ("معرب", 4), + ("hello 世界", 10), + # str+ansi + ("\x1b[34mhello, world\x1b[39m", 12), + ("\x1b[34m世界你好\x1b[39m", 8), + ("\x1b[34m蓝色\x1b[39m", 4), + ("\x1b[34mשלום\x1b[39m", 4), + ("\x1b[34mمعرب\x1b[39m", 4), + ("\x1b[34mhello 世界\x1b[39m", 10), + # colorclass + (Color("{blue}hello, world{/blue}"), 12), + (Color("{blue}世界你好{/blue}"), 8), + (Color("{blue}蓝色{/blue}"), 4), + (Color("{blue}שלום{/blue}"), 4), + (Color("{blue}معرب{/blue}"), 4), + (Color("{blue}hello 世界{/blue}"), 10), + # colorama + (Fore.BLUE + "hello, world" + Fore.RESET, 12), + (Fore.BLUE + "世界你好" + Fore.RESET, 8), + (Fore.BLUE + "蓝色" + Fore.RESET, 4), + (Fore.BLUE + "שלום" + Fore.RESET, 4), + (Fore.BLUE + "معرب" + Fore.RESET, 4), + (Fore.BLUE + "hello 世界" + Fore.RESET, 10), + # termcolor + (colored("hello, world", "blue"), 12), + (colored("世界你好", "blue"), 8), + (colored("蓝色", "blue"), 4), + (colored("שלום", "blue"), 4), + (colored("معرب", "blue"), 4), + (colored("hello 世界", "blue"), 10), + ], +) def test(string, expected): """Test function with different color libraries. @@ -2,39 +2,23 @@ name = terminaltables [tox] -envlist = lint,py{34,27,26} +envlist = py{38,39,310,311,312,313} [testenv] commands = - python -c "import os, sys; sys.platform == 'win32' and os.system('easy_install pillow')" - py.test --cov-report term-missing --cov-report xml --cov {[general]name} --cov-config tox.ini {posargs:tests} + python -c "import os, sys; sys.version_info[:3] <= (3, 12, 0) and os.system('pip install pillow')" + py.test --cov-report term-missing --cov-report xml --cov {[general]name} {posargs:tests} deps = - colorama==0.3.7 - colorclass==2.2.0 - pytest-cov==2.4.0 - termcolor==1.1.0 + colorama>=0.3.7 + colorclass>=2.2.0 + pytest-cov>=2.4.0 + termcolor>=1.1.0 passenv = WINDIR setenv = PYTHON_EGG_CACHE = {envtmpdir} usedevelop = True -[testenv:lint] -commands = - python setup.py check --strict - python setup.py check --strict -m - python setup.py check --strict -s - python setup.py check_version - flake8 --application-import-names={[general]name},tests - pylint --rcfile=tox.ini setup.py {[general]name} -deps = - flake8-docstrings==1.0.3 - flake8-import-order==0.12 - flake8==3.3.0 - pep8-naming==0.4.1 - pydocstyle==1.1.1 - pylint==1.6.5 - [testenv:docs] changedir = {toxinidir}/docs commands = @@ -55,25 +39,24 @@ passenv = HOME HOSTNAME SSH_AUTH_SOCK - TRAVIS* USER usedevelop = False - -[flake8] -exclude = .tox/*,build/*,docs/*,env/*,get-pip.py -import-order-style = smarkets -max-line-length = 120 -statistics = True - -[pylint] -disable = - locally-disabled, - too-few-public-methods, - too-many-instance-attributes, -ignore = .tox/*,build/*,docs/*,env/*,get-pip.py -max-args = 6 -max-line-length = 120 -reports = no - -[run] -branch = True +; +;[flake8] +;exclude = .tox/*,build/*,docs/*,env/*,get-pip.py +;import-order-style = smarkets +;max-line-length = 120 +;statistics = True + +;[pylint] +;disable = +; locally-disabled, +; too-few-public-methods, +; too-many-instance-attributes, +;ignore = .tox/*,build/*,docs/*,env/*,get-pip.py +;max-args = 6 +;max-line-length = 120 +;reports = no + +;[run] +;branch = True |