1
0
Fork 0

Merging upstream version 4.0.0 (Closes: #1095814).

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-12 15:00:49 +01:00
parent 7318203982
commit 07735c967b
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
62 changed files with 4173 additions and 2396 deletions

25
.converagerc Normal file
View file

@ -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

View file

@ -1,3 +1,4 @@
export PYTHONIOENCODING=utf-8 export PYTHONIOENCODING=utf-8
export LC_ALL=en_US.UTF-8 export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8 export LANG=en_US.UTF-8
export FORCE_COLOR=true

57
.github/workflows/build.yml vendored Normal file
View file

@ -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

2
.gitignore vendored
View file

@ -94,3 +94,5 @@ test*.png
.idea/ .idea/
requirements*.txt requirements*.txt
.DS_Store .DS_Store
.build_history/

95
.pre-commit-config.yaml Normal file
View file

@ -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

633
.pylintrc Normal file
View file

@ -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

View file

@ -4,7 +4,20 @@ Changelog
This project adheres to `Semantic Versioning <http://semver.org/>`_. 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 Added

104
Makefile Normal file
View file

@ -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

16
Pipfile
View file

@ -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"

View file

@ -1,11 +1,19 @@
## terminaltables
# What is it # What is it
Easily draw tables in terminal/console applications from a list of lists of strings. Supports multi-line rows. 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. Tested on Python 3.8+
- Python 2.7, 3.3, 3.4, and 3.5+ supported on Windows (both 32 and 64 bit versions of Python).
**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 📖 Full documentation: https://robpol86.github.io/terminaltables
@ -15,19 +23,19 @@ Quickstart
Install: Install:
```bash ```bash
pip install terminaltables pip install terminaltables3
``` ```
Usage: Usage:
```python ```python
from terminaltables import AsciiTable from terminaltables3 import AsciiTable
table_data = [ table_data = [
['Heading1', 'Heading2'], ["Heading1", "Heading2"],
['row1 column1', 'row1 column2'], ["row1 column1", "row1 column2"],
['row2 column1', 'row2 column2'], ["row2 column1", "row2 column2"],
['row3 column1', 'row3 column2'] ["row3 column1", "row3 column2"],
] ]
table = AsciiTable(table_data) table = AsciiTable(table_data)
print print
@ -54,4 +62,4 @@ Source code for examples:
- [example2.py](https://github.com/matthewdeanmartin/terminaltables/blob/master/example2.py) - [example2.py](https://github.com/matthewdeanmartin/terminaltables/blob/master/example2.py)
- [example3.py](https://github.com/matthewdeanmartin/terminaltables/blob/master/example3.py) - [example3.py](https://github.com/matthewdeanmartin/terminaltables/blob/master/example3.py)
[Change Log](https://github.com/matthewdeanmartin/terminaltables/blob/master/CHANGELOG.md) [Change Log](https://github.com/matthewdeanmartin/terminaltables/blob/master/CHANGELOG.md)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,
)

View file

@ -6,50 +6,55 @@ import time
# General configuration. # General configuration.
sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))) sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), "..")))
author = '@Robpol86' author = "@Robpol86"
copyright = '{}, {}'.format(time.strftime('%Y'), author) copyright = "{}, {}".format(time.strftime("%Y"), author)
html_last_updated_fmt = '%c {}'.format(time.tzname[time.localtime().tm_isdst]) html_last_updated_fmt = f"%c {time.tzname[time.localtime().tm_isdst]}"
master_doc = 'index' master_doc = "index"
project = __import__('setup').NAME project = __import__("setup").NAME
pygments_style = 'friendly' pygments_style = "friendly"
release = version = __import__('setup').VERSION release = version = __import__("setup").VERSION
templates_path = ['_templates'] templates_path = ["_templates"]
extensions = list() extensions = []
# Options for HTML output. # Options for HTML output.
html_context = dict( html_context = dict(
conf_py_path='/docs/', conf_py_path="/docs/",
display_github=True, display_github=True,
github_repo=os.environ.get('TRAVIS_REPO_SLUG', '/' + project).split('/', 1)[1], 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_user=os.environ.get("TRAVIS_REPO_SLUG", "robpol86/").split("/", 1)[0],
github_version=os.environ.get('TRAVIS_BRANCH', 'master'), github_version=os.environ.get("TRAVIS_BRANCH", "master"),
source_suffix='.rst', source_suffix=".rst",
) )
html_copy_source = False html_copy_source = False
html_favicon = 'favicon.ico' html_favicon = "favicon.ico"
html_theme = 'sphinx_rtd_theme' html_theme = "sphinx_rtd_theme"
html_title = project html_title = project
# autodoc # autodoc
extensions.append('sphinx.ext.autodoc') extensions.append("sphinx.ext.autodoc")
# extlinks. # extlinks.
extensions.append('sphinx.ext.extlinks') extensions.append("sphinx.ext.extlinks")
extlinks = {'github': ('https://github.com/robpol86/{0}/blob/v{1}/%s'.format(project, version), '')} extlinks = {
"github": (
f"https://github.com/robpol86/{project}/blob/v{version}/%s",
"",
)
}
# google analytics # google analytics
extensions.append('sphinxcontrib.googleanalytics') extensions.append("sphinxcontrib.googleanalytics")
googleanalytics_id = 'UA-82627369-1' googleanalytics_id = "UA-82627369-1"
# SCVersioning. # SCVersioning.
scv_banner_greatest_tag = True scv_banner_greatest_tag = True
scv_grm_exclude = ('.gitignore', '.nojekyll', 'README.rst') scv_grm_exclude = (".gitignore", ".nojekyll", "README.rst")
scv_overflow = ('-W',) scv_overflow = ("-W",)
scv_show_banner = True scv_show_banner = True
scv_sort = ('semver', 'time') scv_sort = ("semver", "time")

View file

@ -4,39 +4,38 @@
Just prints sample text and exits. 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 = ( TABLE_DATA = (
('Platform', 'Years', 'Notes'), ("Platform", "Years", "Notes"),
('Mk5', '2007-2009', 'The Golf Mk5 Variant was\nintroduced in 2007.'), ("Mk5", "2007-2009", "The Golf Mk5 Variant was\nintroduced in 2007."),
('MKVI', '2009-2013', 'Might actually be Mk5.'), ("MKVI", "2009-2013", "Might actually be Mk5."),
) )
def main(): def main():
"""Main function.""" """Main function."""
title = 'Jetta SportWagen' title = "Jetta SportWagen"
# AsciiTable. # AsciiTable.
table_instance = AsciiTable(TABLE_DATA, title) table_instance = AsciiTable(TABLE_DATA, title)
table_instance.justify_columns[2] = 'right' table_instance.justify_columns[2] = "right"
print(table_instance.table) print(table_instance.table)
print() print()
# SingleTable. # SingleTable.
table_instance = SingleTable(TABLE_DATA, title) table_instance = SingleTable(TABLE_DATA, title)
table_instance.justify_columns[2] = 'right' table_instance.justify_columns[2] = "right"
print(table_instance.table) print(table_instance.table)
print() print()
# DoubleTable. # DoubleTable.
table_instance = DoubleTable(TABLE_DATA, title) table_instance = DoubleTable(TABLE_DATA, title)
table_instance.justify_columns[2] = 'right' table_instance.justify_columns[2] = "right"
print(table_instance.table) print(table_instance.table)
print() print()
if __name__ == '__main__': if __name__ == "__main__":
main() main()

View file

@ -4,19 +4,21 @@
Just prints sample text and exits. Just prints sample text and exits.
""" """
from __future__ import print_function
from colorclass import Color, Windows from colorclass import Color, Windows
from terminaltables import SingleTable from terminaltables3 import SingleTable
def table_server_timings(): def table_server_timings():
"""Return table string to be printed.""" """Return table string to be printed."""
table_data = [ table_data = [
[Color('{autogreen}<10ms{/autogreen}'), '192.168.0.100, 192.168.0.101'], [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("{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 = SingleTable(table_data)
table_instance.inner_heading_row_border = False table_instance.inner_heading_row_border = False
@ -26,20 +28,32 @@ def table_server_timings():
def table_server_status(): def table_server_status():
"""Return table string to be printed.""" """Return table string to be printed."""
table_data = [ 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("Low Space"),
[Color('{autocyan}Low Free RAM{/autocyan}'), Color('Nominal Free RAM'), Color('High Free RAM')], 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_heading_row_border = False
table_instance.inner_row_border = True 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 return table_instance.table
def table_abcd(): def table_abcd():
"""Return table string to be printed. Two tables on one line.""" """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. # Get first table lines.
table_instance.outer_border = False table_instance.outer_border = False
@ -53,16 +67,18 @@ def table_abcd():
# Combine. # Combine.
smallest, largest = sorted([table_inner_borders, table_outer_borders], key=len) smallest, largest = sorted([table_inner_borders, table_outer_borders], key=len)
smallest += [''] * (len(largest) - len(smallest)) # Make both same size. smallest += [""] * (len(largest) - len(smallest)) # Make both same size.
combined = list() combined = []
for i, row in enumerate(largest): for i, row in enumerate(largest):
combined.append(row.ljust(10) + ' ' + smallest[i]) combined.append(row.ljust(10) + " " + smallest[i])
return '\n'.join(combined) return "\n".join(combined)
def main(): def main():
"""Main function.""" """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. # Server timings.
print(table_server_timings()) print(table_server_timings())
@ -77,10 +93,10 @@ def main():
print() print()
# Instructions. # Instructions.
table_instance = SingleTable([['Obey Obey Obey Obey']], 'Instructions') table_instance = SingleTable([["Obey Obey Obey Obey"]], "Instructions")
print(table_instance.table) print(table_instance.table)
print() print()
if __name__ == '__main__': if __name__ == "__main__":
main() main()

View file

@ -4,33 +4,37 @@
Just prints sample text and exits. Just prints sample text and exits.
""" """
from __future__ import print_function
from textwrap import wrap 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 ' LONG_STRING = (
'et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut ' "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore "
'aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum ' "et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut "
'dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui ' "aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum "
'officia deserunt mollit anim id est laborum.') "dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui "
"officia deserunt mollit anim id est laborum."
)
def main(): def main():
"""Main function.""" """Main function."""
table_data = [ 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) table = SingleTable(table_data)
# Calculate newlines. # Calculate newlines.
max_width = table.column_max_width(1) 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 table.table_data[0][1] = wrapped_string
print(table.table) print(table.table)
if __name__ == '__main__': if __name__ == "__main__":
main() main()

1058
poetry.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -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"

View file

@ -1,7 +1,7 @@
[tool.poetry] [tool.poetry]
name = "terminaltables" name = "terminaltables3"
version = "3.1.10" version = "4.0.0"
description = "Generate simple tables in terminals from a nested list of strings." description = "Generate simple tables in terminals from a nested list of strings. Fork of terminaltables."
authors = [ authors = [
"Robpol86 <robpol86@gmail.com>", "Robpol86 <robpol86@gmail.com>",
"Matthew Martin <matthewdeanmartin@gmail.com>" "Matthew Martin <matthewdeanmartin@gmail.com>"
@ -18,49 +18,74 @@ classifiers = [
"Operating System :: Microsoft :: Windows", "Operating System :: Microsoft :: Windows",
"Operating System :: POSIX :: Linux", "Operating System :: POSIX :: Linux",
"Operating System :: POSIX", "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.8",
"Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10", "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", "Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries",
"Topic :: Terminals", "Topic :: Terminals",
"Topic :: Text Processing :: Markup", "Topic :: Text Processing :: Markup",
] ]
packages = [ packages = [
{ include = "terminaltables" }, { include = "terminaltables3" },
] ]
include = [ include = [
"terminaltables/**/*.py", "terminaltables3/**/*.py",
] ]
exclude = [ exclude = [
] ]
license = "MIT" license = "MIT"
readme = "README.md" readme = "README.md"
repository = "https://github.com/matthewdeanmartin/terminaltables" repository = "https://github.com/matthewdeanmartin/terminaltables3"
homepage = "https://github.com/matthewdeanmartin/terminaltables" homepage = "https://github.com/matthewdeanmartin/terminaltables3"
documentation = "https://github.com/matthewdeanmartin/terminaltables" documentation = "https://github.com/matthewdeanmartin/terminaltables3"
[tool.poetry.urls] [tool.poetry.urls]
"Bug Tracker" = "https://github.com/matthewdeanmartin/terminaltables/issues" "Bug Tracker" = "https://github.com/matthewdeanmartin/terminaltables3/issues"
"Change Log" = "https://github.com/matthewdeanmartin/terminaltables/blob/master/CHANGELOG.md" "Change Log" = "https://github.com/matthewdeanmartin/terminaltables3/blob/master/CHANGELOG.md"
[tool.poetry.scripts] [tool.poetry.scripts]
[tool.poetry.dependencies] [tool.poetry.dependencies]
# per vermin's estimation # per vermin's estimation
python = ">=2.6 || >=3.0" python = ">=3.8"
[tool.poetry.dev-dependencies] [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] [build-system]
requires = ["poetry>=0.12"] requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.masonry.api" build-backend = "poetry.core.masonry.api"

View file

@ -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'

View file

@ -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

View file

@ -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"

View file

@ -1,8 +1,12 @@
"""AsciiTable is the main table class. To be inherited by other tables. Define convenience methods here.""" """AsciiTable is the main table class. To be inherited by other tables. Define convenience methods here."""
from terminaltables.base_table import BaseTable from terminaltables3.base_table import BaseTable
from terminaltables.terminal_io import terminal_size from terminaltables3.terminal_io import terminal_size
from terminaltables.width_and_alignment import column_max_width, max_dimensions, table_width from terminaltables3.width_and_alignment import (
column_max_width,
max_dimensions,
table_width,
)
class AsciiTable(BaseTable): 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. :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. """Return the maximum width of a column based on the current terminal width.
:param int column_number: The column number to query. :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 outer_border = 2 if self.outer_border else 0
inner_border = 1 if self.inner_column_border else 0 inner_border = 1 if self.inner_column_border else 0
padding = self.padding_left + self.padding_right 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 @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.""" """Return a list of integers representing the widths of each table column without padding."""
if not self.table_data: if not self.table_data:
return list() return []
return max_dimensions(self.table_data)[0] return max_dimensions(self.table_data)[0]
@property @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 True if the table fits within the terminal width, False if the table breaks."""
return self.table_width <= terminal_size()[0] return self.table_width <= terminal_size()[0]
@property @property
def table_width(self): def table_width(self) -> int:
"""Return the width of the table including padding and borders.""" """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 outer_border = 2 if self.outer_border else 0
inner_border = 1 if self.inner_column_border else 0 inner_border = 1 if self.inner_column_border else 0
return table_width(outer_widths, outer_border, inner_border) return table_width(outer_widths, outer_border, inner_border)

View file

@ -1,10 +1,12 @@
"""Base table class. Define just the bare minimum to build tables.""" """Base table class. Define just the bare minimum to build tables."""
from terminaltables.build import build_border, build_row, flatten from typing import Generator, Optional, Sequence, Tuple
from terminaltables.width_and_alignment import align_and_pad_cell, max_dimensions
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. """Base table class.
:ivar iter table_data: List (empty or list of lists of strings) representing the table. :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. :ivar int padding_right: Number of spaces to pad on the right side of every cell.
""" """
CHAR_F_INNER_HORIZONTAL = '-' CHAR_F_INNER_HORIZONTAL = "-"
CHAR_F_INNER_INTERSECT = '+' CHAR_F_INNER_INTERSECT = "+"
CHAR_F_INNER_VERTICAL = '|' CHAR_F_INNER_VERTICAL = "|"
CHAR_F_OUTER_LEFT_INTERSECT = '+' CHAR_F_OUTER_LEFT_INTERSECT = "+"
CHAR_F_OUTER_LEFT_VERTICAL = '|' CHAR_F_OUTER_LEFT_VERTICAL = "|"
CHAR_F_OUTER_RIGHT_INTERSECT = '+' CHAR_F_OUTER_RIGHT_INTERSECT = "+"
CHAR_F_OUTER_RIGHT_VERTICAL = '|' CHAR_F_OUTER_RIGHT_VERTICAL = "|"
CHAR_H_INNER_HORIZONTAL = '-' CHAR_H_INNER_HORIZONTAL = "-"
CHAR_H_INNER_INTERSECT = '+' CHAR_H_INNER_INTERSECT = "+"
CHAR_H_INNER_VERTICAL = '|' CHAR_H_INNER_VERTICAL = "|"
CHAR_H_OUTER_LEFT_INTERSECT = '+' CHAR_H_OUTER_LEFT_INTERSECT = "+"
CHAR_H_OUTER_LEFT_VERTICAL = '|' CHAR_H_OUTER_LEFT_VERTICAL = "|"
CHAR_H_OUTER_RIGHT_INTERSECT = '+' CHAR_H_OUTER_RIGHT_INTERSECT = "+"
CHAR_H_OUTER_RIGHT_VERTICAL = '|' CHAR_H_OUTER_RIGHT_VERTICAL = "|"
CHAR_INNER_HORIZONTAL = '-' CHAR_INNER_HORIZONTAL = "-"
CHAR_INNER_INTERSECT = '+' CHAR_INNER_INTERSECT = "+"
CHAR_INNER_VERTICAL = '|' CHAR_INNER_VERTICAL = "|"
CHAR_OUTER_BOTTOM_HORIZONTAL = '-' CHAR_OUTER_BOTTOM_HORIZONTAL = "-"
CHAR_OUTER_BOTTOM_INTERSECT = '+' CHAR_OUTER_BOTTOM_INTERSECT = "+"
CHAR_OUTER_BOTTOM_LEFT = '+' CHAR_OUTER_BOTTOM_LEFT = "+"
CHAR_OUTER_BOTTOM_RIGHT = '+' CHAR_OUTER_BOTTOM_RIGHT = "+"
CHAR_OUTER_LEFT_INTERSECT = '+' CHAR_OUTER_LEFT_INTERSECT = "+"
CHAR_OUTER_LEFT_VERTICAL = '|' CHAR_OUTER_LEFT_VERTICAL = "|"
CHAR_OUTER_RIGHT_INTERSECT = '+' CHAR_OUTER_RIGHT_INTERSECT = "+"
CHAR_OUTER_RIGHT_VERTICAL = '|' CHAR_OUTER_RIGHT_VERTICAL = "|"
CHAR_OUTER_TOP_HORIZONTAL = '-' CHAR_OUTER_TOP_HORIZONTAL = "-"
CHAR_OUTER_TOP_INTERSECT = '+' CHAR_OUTER_TOP_INTERSECT = "+"
CHAR_OUTER_TOP_LEFT = '+' CHAR_OUTER_TOP_LEFT = "+"
CHAR_OUTER_TOP_RIGHT = '+' CHAR_OUTER_TOP_RIGHT = "+"
def __init__(self, table_data, title=None): def __init__(
self, table_data: Sequence[Sequence[str]], title: Optional[str] = None
):
"""Constructor. """Constructor.
:param iter table_data: List (empty or list of lists of strings) representing the table. :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.inner_row_border = False
self.outer_border = True 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_left = 1
self.padding_right = 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. """Build any kind of horizontal border for the table.
:param str style: Type of border to return. :param str style: Type of border to return.
@ -77,39 +83,45 @@ class BaseTable(object):
:return: Prepared border as a tuple of strings. :return: Prepared border as a tuple of strings.
:rtype: tuple :rtype: tuple
""" """
if style == 'top': if style == "top":
horizontal = self.CHAR_OUTER_TOP_HORIZONTAL horizontal = self.CHAR_OUTER_TOP_HORIZONTAL
left = self.CHAR_OUTER_TOP_LEFT 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 right = self.CHAR_OUTER_TOP_RIGHT
title = self.title title = self.title
elif style == 'bottom': elif style == "bottom":
horizontal = self.CHAR_OUTER_BOTTOM_HORIZONTAL horizontal = self.CHAR_OUTER_BOTTOM_HORIZONTAL
left = self.CHAR_OUTER_BOTTOM_LEFT 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 right = self.CHAR_OUTER_BOTTOM_RIGHT
title = None title = None
elif style == 'heading': elif style == "heading":
horizontal = self.CHAR_H_INNER_HORIZONTAL horizontal = self.CHAR_H_INNER_HORIZONTAL
left = self.CHAR_H_OUTER_LEFT_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 '' intersect = self.CHAR_H_INNER_INTERSECT if self.inner_column_border else ""
right = self.CHAR_H_OUTER_RIGHT_INTERSECT if self.outer_border else '' right = self.CHAR_H_OUTER_RIGHT_INTERSECT if self.outer_border else ""
title = None title = None
elif style == 'footing': elif style == "footing":
horizontal = self.CHAR_F_INNER_HORIZONTAL horizontal = self.CHAR_F_INNER_HORIZONTAL
left = self.CHAR_F_OUTER_LEFT_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 '' intersect = self.CHAR_F_INNER_INTERSECT if self.inner_column_border else ""
right = self.CHAR_F_OUTER_RIGHT_INTERSECT if self.outer_border else '' right = self.CHAR_F_OUTER_RIGHT_INTERSECT if self.outer_border else ""
title = None title = None
else: else:
horizontal = self.CHAR_INNER_HORIZONTAL horizontal = self.CHAR_INNER_HORIZONTAL
left = self.CHAR_OUTER_LEFT_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 '' intersect = self.CHAR_INNER_INTERSECT if self.inner_column_border else ""
right = self.CHAR_OUTER_RIGHT_INTERSECT if self.outer_border else '' right = self.CHAR_OUTER_RIGHT_INTERSECT if self.outer_border else ""
title = None title = None
return build_border(outer_widths, horizontal, left, intersect, right, title) 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. 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 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. :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. # Resize row if it doesn't have enough cells.
if len(row) != len(inner_widths): 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. # Pad and align each cell. Split each cell into lines to support multi-line cells.
for i, cell in enumerate(row): for i, cell in enumerate(row):
align = (self.justify_columns.get(i),) align = (self.justify_columns.get(i),)
inner_dimensions = (inner_widths[i], height) inner_dimensions = (inner_widths[i], height)
padding = (self.padding_left, self.padding_right, 0, 0) 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. # Determine border characters.
if style == 'heading': if style == "heading":
left = self.CHAR_H_OUTER_LEFT_VERTICAL if self.outer_border else '' left = self.CHAR_H_OUTER_LEFT_VERTICAL if self.outer_border else ""
center = self.CHAR_H_INNER_VERTICAL if self.inner_column_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 '' right = self.CHAR_H_OUTER_RIGHT_VERTICAL if self.outer_border else ""
elif style == 'footing': elif style == "footing":
left = self.CHAR_F_OUTER_LEFT_VERTICAL if self.outer_border else '' left = self.CHAR_F_OUTER_LEFT_VERTICAL if self.outer_border else ""
center = self.CHAR_F_INNER_VERTICAL if self.inner_column_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 '' right = self.CHAR_F_OUTER_RIGHT_VERTICAL if self.outer_border else ""
else: else:
left = self.CHAR_OUTER_LEFT_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 '' center = self.CHAR_INNER_VERTICAL if self.inner_column_border else ""
right = self.CHAR_OUTER_RIGHT_VERTICAL if self.outer_border else '' right = self.CHAR_OUTER_RIGHT_VERTICAL if self.outer_border else ""
# Yield each line. # Yield each line.
for line in build_row(cells_in_row, left, center, right): yield from build_row(cells_in_row, left, center, right)
yield line
def gen_table(self, inner_widths, inner_heights, outer_widths): 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. """Combine everything and yield every line of the entire table with borders.
:param iter inner_widths: List of widths (no padding) for each column. :param iter inner_widths: List of widths (no padding) for each column.
@ -178,7 +196,7 @@ class BaseTable(object):
""" """
# Yield top border. # Yield top border.
if self.outer_border: if self.outer_border:
yield self.horizontal_border('top', outer_widths) yield self.horizontal_border("top", outer_widths)
# Yield table body. # Yield table body.
row_count = len(self.table_data) row_count = len(self.table_data)
@ -186,32 +204,33 @@ class BaseTable(object):
for i, row in enumerate(self.table_data): for i, row in enumerate(self.table_data):
# Yield the row line by line (e.g. multi-line rows). # Yield the row line by line (e.g. multi-line rows).
if self.inner_heading_row_border and i == 0: if self.inner_heading_row_border and i == 0:
style = 'heading' style = "heading"
elif self.inner_footing_row_border and i == last_row_index: elif self.inner_footing_row_border and i == last_row_index:
style = 'footing' style = "footing"
else: else:
style = 'row' style = "row"
for line in self.gen_row_lines(row, style, inner_widths, inner_heights[i]): yield from self.gen_row_lines(row, style, inner_widths, inner_heights[i])
yield line
# If this is the last row then break. No separator needed. # If this is the last row then break. No separator needed.
if i == last_row_index: if i == last_row_index:
break break
# Yield heading separator. # Yield heading separator.
if self.inner_heading_row_border and i == 0: 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. # Yield footing separator.
elif self.inner_footing_row_border and i == before_last_row_index: 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. # Yield row separator.
elif self.inner_row_border: elif self.inner_row_border:
yield self.horizontal_border('row', outer_widths) yield self.horizontal_border("row", outer_widths)
# Yield bottom border. # Yield bottom border.
if self.outer_border: if self.outer_border:
yield self.horizontal_border('bottom', outer_widths) yield self.horizontal_border("bottom", outer_widths)
@property @property
def table(self): def table(self) -> str:
"""Return a large string of the entire table ready to be printed to the terminal.""" """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)) return flatten(self.gen_table(*dimensions))

View file

@ -1,9 +1,18 @@
"""Combine cells into rows.""" """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`. """Zip borders between items in `line`.
e.g. ('l', '1', 'c', '2', 'c', '3', 'r') e.g. ('l', '1', 'c', '2', 'c', '3', 'r')
@ -41,15 +50,21 @@ def combine(line, left, intersect, right):
yield intersect yield intersect
item = peek item = peek
else: else:
for i in line: yield from line
yield i
# Yield right border. # Yield right border.
if right: if right:
yield 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. """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. 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. # Handle title fitting in the first column.
if length == outer_widths[0]: 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]: 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) return combine(columns, left, intersect, right)
# Handle wide titles/narrow columns. # 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): for width in combine(outer_widths, None, bool(intersect), None):
# If title is taken care of. # If title is taken care of.
if length < 1: 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. # If title's last character overrides an intersect character.
elif width is True and length == 1: elif width is True and length == 1:
length = 0 length = 0
@ -105,7 +126,9 @@ def build_border(outer_widths, horizontal, left, intersect, right, title=None):
length -= 1 length -= 1
# If title's last character is within a column. # If title's last character is within a column.
elif width >= length: 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 length = 0
# If remainder of title won't fit in a column. # If remainder of title won't fit in a column.
else: else:
@ -148,4 +171,4 @@ def flatten(table):
:return: Joined rows/cells. :return: Joined rows/cells.
:rtype: str :rtype: str
""" """
return '\n'.join(''.join(r) for r in table) return "\n".join("".join(r) for r in table)

View file

@ -1,7 +1,9 @@
"""GithubFlavoredMarkdownTable class.""" """GithubFlavoredMarkdownTable class."""
from terminaltables.ascii_table import AsciiTable from typing import Sequence
from terminaltables.build import combine
from terminaltables3.ascii_table import AsciiTable
from terminaltables3.build import combine
class GithubFlavoredMarkdownTable(AsciiTable): 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. :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. """Constructor.
:param iter table_data: List (empty or list of lists of strings) representing the table. :param iter table_data: List (empty or list of lists of strings) representing the table.
""" """
# Github flavored markdown table won't support title. # Github flavored markdown table won't support title.
super(GithubFlavoredMarkdownTable, self).__init__(table_data) super().__init__(table_data)
def horizontal_border(self, _, outer_widths): def horizontal_border(self, _, outer_widths):
"""Handle the GitHub heading border. """Handle the GitHub heading border.
@ -38,16 +40,18 @@ class GithubFlavoredMarkdownTable(AsciiTable):
intersect = self.CHAR_INNER_VERTICAL intersect = self.CHAR_INNER_VERTICAL
right = self.CHAR_OUTER_RIGHT_VERTICAL right = self.CHAR_OUTER_RIGHT_VERTICAL
columns = list() columns = []
for i, width in enumerate(outer_widths): for i, width in enumerate(outer_widths):
justify = self.justify_columns.get(i) justify = self.justify_columns.get(i)
width = max(3, width) # Width should be at least 3 so justification can be applied. width = max(
if justify == 'left': 3, width
columns.append(':' + horizontal * (width - 1)) ) # Width should be at least 3 so justification can be applied.
elif justify == 'right': if justify == "left":
columns.append(horizontal * (width - 1) + ':') columns.append(":" + horizontal * (width - 1))
elif justify == 'center': elif justify == "right":
columns.append(':' + horizontal * (width - 2) + ':') columns.append(horizontal * (width - 1) + ":")
elif justify == "center":
columns.append(":" + horizontal * (width - 2) + ":")
else: else:
columns.append(horizontal * width) columns.append(horizontal * width)
@ -63,8 +67,7 @@ class GithubFlavoredMarkdownTable(AsciiTable):
""" """
for i, row in enumerate(self.table_data): for i, row in enumerate(self.table_data):
# Yield the row line by line (e.g. multi-line rows). # 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 from self.gen_row_lines(row, "row", inner_widths, inner_heights[i])
yield line
# Yield heading separator. # Yield heading separator.
if i == 0: if i == 0:
yield self.horizontal_border(None, outer_widths) yield self.horizontal_border(None, outer_widths)

View file

@ -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

View file

@ -3,16 +3,17 @@
import ctypes import ctypes
import struct import struct
import sys import sys
from typing import Tuple, Union
DEFAULT_HEIGHT = 24 DEFAULT_HEIGHT = 24
DEFAULT_WIDTH = 79 DEFAULT_WIDTH = 79
INVALID_HANDLE_VALUE = -1 INVALID_HANDLE_VALUE = -1
IS_WINDOWS = sys.platform == 'win32' IS_WINDOWS = sys.platform == "win32"
STD_ERROR_HANDLE = -12 STD_ERROR_HANDLE = -12
STD_OUTPUT_HANDLE = -11 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). """Get information about this current console window (Windows only).
https://github.com/Robpol86/colorclass/blob/ab42da59/colorclass/windows.py#L111 https://github.com/Robpol86/colorclass/blob/ab42da59/colorclass/windows.py#L111
@ -26,7 +27,7 @@ def get_console_info(kernel32, handle):
:rtype: tuple :rtype: tuple
""" """
if handle == INVALID_HANDLE_VALUE: if handle == INVALID_HANDLE_VALUE:
raise OSError('Invalid handle.') raise OSError("Invalid handle.")
# Query Win32 API. # Query Win32 API.
lpcsbi = ctypes.create_string_buffer(22) # Populated by GetConsoleScreenBufferInfo. 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. raise ctypes.WinError() # Subclass of OSError.
# Parse data. # 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 width, height = right - left, bottom - top
return width, height return width, height
def terminal_size(kernel32=None): def terminal_size(kernel32=None) -> Tuple[int, int]:
"""Get the width and height of the terminal. """Get the width and height of the terminal.
http://code.activestate.com/recipes/440694-determine-size-of-console-window-on-windows/ 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)) return get_console_info(kernel32, kernel32.GetStdHandle(STD_ERROR_HANDLE))
except OSError: except OSError:
try: try:
return get_console_info(kernel32, kernel32.GetStdHandle(STD_OUTPUT_HANDLE)) return get_console_info(
kernel32, kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
)
except OSError: except OSError:
return DEFAULT_WIDTH, DEFAULT_HEIGHT return DEFAULT_WIDTH, DEFAULT_HEIGHT
try: try:
device = __import__('fcntl').ioctl(0, __import__('termios').TIOCGWINSZ, '\0\0\0\0\0\0\0\0') device = __import__("fcntl").ioctl(
except IOError: 0, __import__("termios").TIOCGWINSZ, "\0\0\0\0\0\0\0\0"
)
except OSError:
return DEFAULT_WIDTH, DEFAULT_HEIGHT return DEFAULT_WIDTH, DEFAULT_HEIGHT
height, width = struct.unpack('hhhh', device)[:2] height, width = struct.unpack("hhhh", device)[:2]
return width, height 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. """Set the terminal title.
:param title: The title to set (string, unicode, bytes accepted). :param title: The title to set (string, unicode, bytes accepted).
@ -78,7 +83,7 @@ def set_terminal_title(title, kernel32=None):
:rtype: bool :rtype: bool
""" """
try: try:
title_bytes = title.encode('utf-8') title_bytes = title.encode("utf-8")
except AttributeError: except AttributeError:
title_bytes = title title_bytes = title
@ -90,9 +95,8 @@ def set_terminal_title(title, kernel32=None):
is_ascii = all(c < 128 for c in title) # bytes. is_ascii = all(c < 128 for c in title) # bytes.
if is_ascii: if is_ascii:
return kernel32.SetConsoleTitleA(title_bytes) != 0 return kernel32.SetConsoleTitleA(title_bytes) != 0
else: return kernel32.SetConsoleTitleW(title) != 0
return kernel32.SetConsoleTitleW(title) != 0
# Linux/OSX. # Linux/OSX.
sys.stdout.write(b'\033]0;' + title_bytes + b'\007') sys.stdout.write(b"\033]0;" + title_bytes + b"\007")
return True return True

View file

@ -2,36 +2,37 @@
import re import re
import unicodedata 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. """Get the visible width of a unicode string.
Some CJK unicode characters are more than one byte unlike ASCII and latin unicode characters. 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. :param str string: String to measure.
:return: String's width. :return: String's width.
:rtype: int :rtype: int
""" """
if '\033' in string: if "\033" in string:
string = RE_COLOR_ANSI.sub('', string) string = RE_COLOR_ANSI.sub("", string)
# Convert to unicode. # Convert to unicode.
try: try:
string = string.decode('u8') string = string.decode("u8")
except (AttributeError, UnicodeEncodeError): except (AttributeError, UnicodeEncodeError):
pass pass
width = 0 width = 0
for char in string: for char in string:
if unicodedata.east_asian_width(char) in ('F', 'W'): if unicodedata.east_asian_width(char) in ("F", "W"):
width += 2 width += 2
else: else:
width += 1 width += 1
@ -39,7 +40,13 @@ def visible_width(string):
return width 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. """Align a string horizontally and vertically. Also add additional padding in both dimensions.
:param str string: Input string to operate on. :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. :return: Padded cell split into lines.
:rtype: list :rtype: list
""" """
if not hasattr(string, 'splitlines'): if not hasattr(string, "splitlines"):
string = str(string) string = str(string)
# Handle trailing newlines or empty strings, str.splitlines() does not satisfy. # Handle trailing newlines or empty strings, str.splitlines() does not satisfy.
lines = string.splitlines() or [''] lines = string.splitlines() or [""]
if string.endswith('\n'): if string.endswith("\n"):
lines.append('') lines.append("")
# Vertically align and pad. # Vertically align and pad.
if 'bottom' in align: if "bottom" in align:
lines = ([''] * (inner_dimensions[1] - len(lines) + padding[2])) + lines + ([''] * padding[3]) lines = (
elif 'middle' in align: ([""] * (inner_dimensions[1] - len(lines) + padding[2]))
+ lines
+ ([""] * padding[3])
)
elif "middle" in align:
delta = inner_dimensions[1] - len(lines) 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: 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. # Horizontally align and pad.
for i, line in enumerate(lines): for i, line in enumerate(lines):
new_width = inner_dimensions[0] + len(line) - visible_width(line) 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]) lines[i] = line.rjust(padding[0] + new_width, space) + (space * padding[1])
elif 'center' in align: elif "center" in align:
lines[i] = (space * padding[0]) + line.center(new_width, space) + (space * padding[1]) lines[i] = (
(space * padding[0])
+ line.center(new_width, space)
+ (space * padding[1])
)
else: else:
lines[i] = (space * padding[0]) + line.ljust(new_width + padding[1], space) lines[i] = (space * padding[0]) + line.ljust(new_width + padding[1], space)
return lines 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. """Get maximum widths of each column and maximum height of each row.
:param iter table_data: List of list of strings (unmodified table data). :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. # Find max width and heights.
for j, row in enumerate(table_data): for j, row in enumerate(table_data):
for i, cell in enumerate(row): 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) cell = str(cell)
if not cell: if not cell:
continue continue
inner_heights[j] = max(inner_heights[j], cell.count('\n') + 1) 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_widths[i] = max(
inner_widths[i],
*[visible_width(the_line) for the_line in cell.splitlines()]
)
# Calculate with padding. # Calculate with padding.
outer_widths = [padding_left + i + padding_right for i in inner_widths] 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 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. """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. :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 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. """Determine the width of the entire table including borders and padding.
:param iter outer_widths: List of widths (with padding) for each column. :param iter outer_widths: List of widths (with padding) for each column.

View file

@ -2,4 +2,4 @@
import py import py
PROJECT_ROOT = py.path.local(__file__).dirpath().join('..') PROJECT_ROOT = py.path.local(__file__).dirpath().join("..")

View file

@ -14,7 +14,7 @@ except ImportError:
from tests import PROJECT_ROOT from tests import PROJECT_ROOT
STARTF_USESHOWWINDOW = getattr(subprocess, 'STARTF_USESHOWWINDOW', 1) STARTF_USESHOWWINDOW = getattr(subprocess, "STARTF_USESHOWWINDOW", 1)
STILL_ACTIVE = 259 STILL_ACTIVE = 259
SW_MAXIMIZE = 3 SW_MAXIMIZE = 3
@ -23,24 +23,24 @@ class StartupInfo(ctypes.Structure):
"""STARTUPINFO structure.""" """STARTUPINFO structure."""
_fields_ = [ _fields_ = [
('cb', ctypes.c_ulong), ("cb", ctypes.c_ulong),
('lpReserved', ctypes.c_char_p), ("lpReserved", ctypes.c_char_p),
('lpDesktop', ctypes.c_char_p), ("lpDesktop", ctypes.c_char_p),
('lpTitle', ctypes.c_char_p), ("lpTitle", ctypes.c_char_p),
('dwX', ctypes.c_ulong), ("dwX", ctypes.c_ulong),
('dwY', ctypes.c_ulong), ("dwY", ctypes.c_ulong),
('dwXSize', ctypes.c_ulong), ("dwXSize", ctypes.c_ulong),
('dwYSize', ctypes.c_ulong), ("dwYSize", ctypes.c_ulong),
('dwXCountChars', ctypes.c_ulong), ("dwXCountChars", ctypes.c_ulong),
('dwYCountChars', ctypes.c_ulong), ("dwYCountChars", ctypes.c_ulong),
('dwFillAttribute', ctypes.c_ulong), ("dwFillAttribute", ctypes.c_ulong),
('dwFlags', ctypes.c_ulong), ("dwFlags", ctypes.c_ulong),
('wShowWindow', ctypes.c_ushort), ("wShowWindow", ctypes.c_ushort),
('cbReserved2', ctypes.c_ushort), ("cbReserved2", ctypes.c_ushort),
('lpReserved2', ctypes.c_char_p), ("lpReserved2", ctypes.c_char_p),
('hStdInput', ctypes.c_ulong), ("hStdInput", ctypes.c_ulong),
('hStdOutput', ctypes.c_ulong), ("hStdOutput", ctypes.c_ulong),
('hStdError', ctypes.c_ulong), ("hStdError", ctypes.c_ulong),
] ]
def __init__(self, maximize=False, title=None): 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 bool maximize: Start process in new console window, maximized.
:param bytes title: Set new window title to this instead of exe path. :param bytes title: Set new window title to this instead of exe path.
""" """
super(StartupInfo, self).__init__() super().__init__()
self.cb = ctypes.sizeof(self) self.cb = ctypes.sizeof(self)
if maximize: if maximize:
self.dwFlags |= STARTF_USESHOWWINDOW self.dwFlags |= STARTF_USESHOWWINDOW
@ -62,14 +62,14 @@ class ProcessInfo(ctypes.Structure):
"""PROCESS_INFORMATION structure.""" """PROCESS_INFORMATION structure."""
_fields_ = [ _fields_ = [
('hProcess', ctypes.c_void_p), ("hProcess", ctypes.c_void_p),
('hThread', ctypes.c_void_p), ("hThread", ctypes.c_void_p),
('dwProcessId', ctypes.c_ulong), ("dwProcessId", ctypes.c_ulong),
('dwThreadId', 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. """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 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. :param bytes title: Set new window title to this. Needed by user32.FindWindow.
""" """
if title is None: 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.startup_info = StartupInfo(maximize=maximized, title=title)
self.process_info = ProcessInfo() self.process_info = ProcessInfo()
self.command_str = subprocess.list2cmdline(command).encode('ascii') self.command_str = subprocess.list2cmdline(command).encode("ascii")
self._handles = list() self._handles = []
self._kernel32 = ctypes.LibraryLoader(ctypes.WinDLL).kernel32 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 self._kernel32.GetExitCodeProcess.restype = ctypes.c_long
def __del__(self): def __del__(self):
"""Close win32 handles.""" """Close win32 handles."""
while self._handles: while self._handles:
try: 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: except IndexError:
break break
@ -111,9 +118,9 @@ class RunNewConsole(object):
False, # bInheritHandles False, # bInheritHandles
subprocess.CREATE_NEW_CONSOLE, # dwCreationFlags subprocess.CREATE_NEW_CONSOLE, # dwCreationFlags
None, # lpEnvironment None, # lpEnvironment
str(PROJECT_ROOT).encode('ascii'), # lpCurrentDirectory str(PROJECT_ROOT).encode("ascii"), # lpCurrentDirectory
ctypes.byref(self.startup_info), # lpStartupInfo ctypes.byref(self.startup_info), # lpStartupInfo
ctypes.byref(self.process_info) # lpProcessInformation ctypes.byref(self.process_info), # lpProcessInformation
): ):
raise ctypes.WinError() raise ctypes.WinError()
@ -125,7 +132,9 @@ class RunNewConsole(object):
self.hwnd = 0 self.hwnd = 0
for _ in range(int(5 / 0.1)): for _ in range(int(5 / 0.1)):
# Takes time for console window to initialize. # 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: if self.hwnd:
break break
time.sleep(0.1) time.sleep(0.1)
@ -141,7 +150,9 @@ class RunNewConsole(object):
status = ctypes.c_ulong(STILL_ACTIVE) status = ctypes.c_ulong(STILL_ACTIVE)
while status.value == STILL_ACTIVE: while status.value == STILL_ACTIVE:
time.sleep(0.1) 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() raise ctypes.WinError()
assert status.value == 0 assert status.value == 0
finally: finally:
@ -154,9 +165,11 @@ class RunNewConsole(object):
:return: Yields region the new window is in (left, upper, right, lower). :return: Yields region the new window is in (left, upper, right, lower).
:rtype: tuple :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): 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 width, height = right - left, bottom - top
assert width > 1 assert width > 1
assert height > 1 assert height > 1
@ -179,8 +192,7 @@ def iter_rows(pil_image):
:rtype: tuple :rtype: tuple
""" """
iterator = izip(*(iter(pil_image.getdata()),) * pil_image.width) iterator = izip(*(iter(pil_image.getdata()),) * pil_image.width)
for row in iterator: yield from iterator
yield row
def get_most_interesting_row(pil_image): 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): for x_pos in range(screenshot.width - si_width + 1):
if row[x_pos] != si_pixel: if row[x_pos] != si_pixel:
continue # First pixel does not match. 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. continue # Row does not match.
# Found match for interesting row of subimg in screenshot. # Found match for interesting row of subimg in screenshot.
y_corrected = y_pos - si_y 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: if list(cropped.getdata()) == si_pixels:
occurrences += 1 occurrences += 1
@ -248,11 +262,12 @@ def try_candidates(screenshot, subimg_candidates, expected_count):
:rtype: int :rtype: int
""" """
from PIL import Image from PIL import Image
count_found = 0 count_found = 0
for subimg_path in subimg_candidates: for subimg_path in subimg_candidates:
with Image.open(subimg_path) as rgba_s: 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. # Make sure subimage isn't too large.
assert subimg.width < 256 assert subimg.width < 256
assert subimg.height < 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. :param iter gen: Generator yielding window position and size to crop screenshot to.
""" """
from PIL import ImageGrab from PIL import ImageGrab
assert save_to.endswith('.png')
assert save_to.endswith(".png")
stop_after = time.time() + timeout stop_after = time.time() + timeout
# Take screenshots until subimage is found. # Take screenshots until subimage is found.
while True: while True:
with ImageGrab.grab(next(gen)) as rgba: with ImageGrab.grab(next(gen)) as rgba:
with rgba.convert(mode='RGB') as screenshot: with rgba.convert(mode="RGB") as screenshot:
count_found = try_candidates(screenshot, subimg_candidates, expected_count) count_found = try_candidates(
screenshot, subimg_candidates, expected_count
)
if count_found == expected_count or time.time() > stop_after: if count_found == expected_count or time.time() > stop_after:
screenshot.save(save_to) screenshot.save(save_to)
assert count_found == expected_count assert count_found == expected_count

View file

@ -6,8 +6,8 @@ from textwrap import dedent
import py import py
import pytest import pytest
from terminaltables import AsciiTable from terminaltables3 import AsciiTable
from terminaltables.terminal_io import IS_WINDOWS from terminaltables3.terminal_io import IS_WINDOWS
from tests import PROJECT_ROOT from tests import PROJECT_ROOT
from tests.screenshot import RunNewConsole, screenshot_until_match from tests.screenshot import RunNewConsole, screenshot_until_match
@ -17,31 +17,31 @@ HERE = py.path.local(__file__).dirpath()
def test_single_line(): def test_single_line():
"""Test single-lined cells.""" """Test single-lined cells."""
table_data = [ table_data = [
['Name', 'Color', 'Type'], ["Name", "Color", "Type"],
['Avocado', 'green', 'nut'], ["Avocado", "green", "nut"],
['Tomato', 'red', 'fruit'], ["Tomato", "red", "fruit"],
['Lettuce', 'green', 'vegetable'], ["Lettuce", "green", "vegetable"],
['Watermelon', 'green'], ["Watermelon", "green"],
[], [],
] ]
table = AsciiTable(table_data, 'Example') table = AsciiTable(table_data, "Example")
table.inner_footing_row_border = True table.inner_footing_row_border = True
table.justify_columns[0] = 'left' table.justify_columns[0] = "left"
table.justify_columns[1] = 'center' table.justify_columns[1] = "center"
table.justify_columns[2] = 'right' table.justify_columns[2] = "right"
actual = table.table actual = table.table
expected = ( expected = (
'+Example-----+-------+-----------+\n' "+Example-----+-------+-----------+\n"
'| Name | Color | Type |\n' "| Name | Color | Type |\n"
'+------------+-------+-----------+\n' "+------------+-------+-----------+\n"
'| Avocado | green | nut |\n' "| Avocado | green | nut |\n"
'| Tomato | red | fruit |\n' "| Tomato | red | fruit |\n"
'| Lettuce | green | vegetable |\n' "| Lettuce | green | vegetable |\n"
'| Watermelon | green | |\n' "| Watermelon | green | |\n"
'+------------+-------+-----------+\n' "+------------+-------+-----------+\n"
'| | | |\n' "| | | |\n"
'+------------+-------+-----------+' "+------------+-------+-----------+"
) )
assert actual == expected assert actual == expected
@ -49,22 +49,25 @@ def test_single_line():
def test_multi_line(): def test_multi_line():
"""Test multi-lined cells.""" """Test multi-lined cells."""
table_data = [ table_data = [
['Show', 'Characters'], ["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'] "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) table = AsciiTable(table_data)
# Test defaults. # Test defaults.
actual = table.table actual = table.table
expected = ( expected = (
'+------------+-------------------------------------------------------------------------------------+\n' "+------------+-------------------------------------------------------------------------------------+\n"
'| Show | Characters |\n' "| Show | Characters |\n"
'+------------+-------------------------------------------------------------------------------------+\n' "+------------+-------------------------------------------------------------------------------------+\n"
'| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n' "| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n"
'| | Dil Pickles |\n' "| | Dil Pickles |\n"
'| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |\n' "| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |\n"
'+------------+-------------------------------------------------------------------------------------+' "+------------+-------------------------------------------------------------------------------------+"
) )
assert actual == expected assert actual == expected
@ -72,52 +75,53 @@ def test_multi_line():
table.inner_row_border = True table.inner_row_border = True
actual = table.table actual = table.table
expected = ( expected = (
'+------------+-------------------------------------------------------------------------------------+\n' "+------------+-------------------------------------------------------------------------------------+\n"
'| Show | Characters |\n' "| Show | Characters |\n"
'+------------+-------------------------------------------------------------------------------------+\n' "+------------+-------------------------------------------------------------------------------------+\n"
'| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n' "| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n"
'| | Dil Pickles |\n' "| | Dil Pickles |\n"
'+------------+-------------------------------------------------------------------------------------+\n' "+------------+-------------------------------------------------------------------------------------+\n"
'| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |\n' "| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |\n"
'+------------+-------------------------------------------------------------------------------------+' "+------------+-------------------------------------------------------------------------------------+"
) )
assert actual == expected assert actual == expected
# Justify right. # Justify right.
table.justify_columns = {1: 'right'} table.justify_columns = {1: "right"}
actual = table.table actual = table.table
expected = ( expected = (
'+------------+-------------------------------------------------------------------------------------+\n' "+------------+-------------------------------------------------------------------------------------+\n"
'| Show | Characters |\n' "| Show | Characters |\n"
'+------------+-------------------------------------------------------------------------------------+\n' "+------------+-------------------------------------------------------------------------------------+\n"
'| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n' "| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n"
'| | Dil Pickles |\n' "| | Dil Pickles |\n"
'+------------+-------------------------------------------------------------------------------------+\n' "+------------+-------------------------------------------------------------------------------------+\n"
'| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |\n' "| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |\n"
'+------------+-------------------------------------------------------------------------------------+' "+------------+-------------------------------------------------------------------------------------+"
) )
assert actual == expected assert actual == expected
@pytest.mark.skipif(str(not IS_WINDOWS)) @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): def test_windows_screenshot(tmpdir):
"""Test on Windows in a new console window. Take a screenshot to verify it works. """Test on Windows in a new console window. Take a screenshot to verify it works.
:param tmpdir: pytest fixture. :param tmpdir: pytest fixture.
""" """
script = tmpdir.join('script.py') script = tmpdir.join("script.py")
command = [sys.executable, str(script)] command = [sys.executable, str(script)]
screenshot = PROJECT_ROOT.join('test_ascii_table.png') screenshot = PROJECT_ROOT.join("test_ascii_table.png")
if screenshot.check(): if screenshot.check():
screenshot.remove() screenshot.remove()
# Generate script. # Generate script.
script_template = dedent(u"""\ script_template = dedent(
"""\
from __future__ import print_function from __future__ import print_function
import os, time import os, time
from colorclass import Color, Windows from colorclass import Color, Windows
from terminaltables import AsciiTable from terminaltables3 import AsciiTable
Windows.enable(auto_colors=True) Windows.enable(auto_colors=True)
stop_after = time.time() + 20 stop_after = time.time() + 20
@ -132,12 +136,13 @@ def test_windows_screenshot(tmpdir):
print('Waiting for screenshot_until_match()...') print('Waiting for screenshot_until_match()...')
while not os.path.exists(r'%s') and time.time() < stop_after: while not os.path.exists(r'%s') and time.time() < stop_after:
time.sleep(0.5) time.sleep(0.5)
""") """
)
script_contents = script_template % str(screenshot) 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. # 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 assert sub_images
# Run. # Run.

View file

@ -6,8 +6,8 @@ from textwrap import dedent
import py import py
import pytest import pytest
from terminaltables import DoubleTable from terminaltables3 import DoubleTable
from terminaltables.terminal_io import IS_WINDOWS from terminaltables3.terminal_io import IS_WINDOWS
from tests import PROJECT_ROOT from tests import PROJECT_ROOT
from tests.screenshot import RunNewConsole, screenshot_until_match from tests.screenshot import RunNewConsole, screenshot_until_match
@ -17,44 +17,35 @@ HERE = py.path.local(__file__).dirpath()
def test_single_line(): def test_single_line():
"""Test single-lined cells.""" """Test single-lined cells."""
table_data = [ table_data = [
['Name', 'Color', 'Type'], ["Name", "Color", "Type"],
['Avocado', 'green', 'nut'], ["Avocado", "green", "nut"],
['Tomato', 'red', 'fruit'], ["Tomato", "red", "fruit"],
['Lettuce', 'green', 'vegetable'], ["Lettuce", "green", "vegetable"],
['Watermelon', 'green'], ["Watermelon", "green"],
[], [],
] ]
table = DoubleTable(table_data, 'Example') table = DoubleTable(table_data, "Example")
table.inner_footing_row_border = True table.inner_footing_row_border = True
table.justify_columns[0] = 'left' table.justify_columns[0] = "left"
table.justify_columns[1] = 'center' table.justify_columns[1] = "center"
table.justify_columns[2] = 'right' table.justify_columns[2] = "right"
actual = table.table actual = table.table
expected = ( expected = (
u'\u2554Example\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550' "\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' "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n"
"\u2551 Name \u2551 Color \u2551 Type \u2551\n"
u'\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"
u'\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550' "\u2551 Avocado \u2551 green \u2551 nut \u2551\n"
u'\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n' "\u2551 Tomato \u2551 red \u2551 fruit \u2551\n"
"\u2551 Lettuce \u2551 green \u2551 vegetable \u2551\n"
u'\u2551 Avocado \u2551 green \u2551 nut \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"
u'\u2551 Tomato \u2551 red \u2551 fruit \u2551\n' "\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n"
"\u2551 \u2551 \u2551 \u2551\n"
u'\u2551 Lettuce \u2551 green \u2551 vegetable \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"
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'
) )
assert actual == expected assert actual == expected
@ -62,47 +53,44 @@ def test_single_line():
def test_multi_line(): def test_multi_line():
"""Test multi-lined cells.""" """Test multi-lined cells."""
table_data = [ table_data = [
['Show', 'Characters'], ["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'] "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) table = DoubleTable(table_data)
# Test defaults. # Test defaults.
actual = table.table actual = table.table
expected = ( expected = (
u'\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550' "\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' "\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' "\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' "\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' "\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' "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n"
"\u2551 Show \u2551 Characters "
u'\u2551 Show \u2551 Characters ' "\u2551\n"
u'\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"
u'\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"
u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' "\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' "\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' "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n"
u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' "\u2551 Rugrats \u2551 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, "
u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n' "\u2551\n"
"\u2551 \u2551 Dil Pickles "
u'\u2551 Rugrats \u2551 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, ' "\u2551\n"
u'\u2551\n' "\u2551 South Park \u2551 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick "
"\u2551\n"
u'\u2551 \u2551 Dil Pickles ' "\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550"
u'\u2551\n' "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
u'\u2551 South Park \u2551 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
u'\u2551\n' "\u2550\u2550\u2550\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"
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'
) )
assert actual == expected assert actual == expected
@ -110,114 +98,101 @@ def test_multi_line():
table.inner_row_border = True table.inner_row_border = True
actual = table.table actual = table.table
expected = ( expected = (
u'\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550' "\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' "\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' "\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' "\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' "\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' "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n"
"\u2551 Show \u2551 Characters "
u'\u2551 Show \u2551 Characters ' "\u2551\n"
u'\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"
u'\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"
u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' "\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' "\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' "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n"
u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' "\u2551 Rugrats \u2551 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, "
u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n' "\u2551\n"
"\u2551 \u2551 Dil Pickles "
u'\u2551 Rugrats \u2551 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, ' "\u2551\n"
u'\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"
u'\u2551 \u2551 Dil Pickles ' "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
u'\u2551\n' "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
u'\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\u2563\n"
u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' "\u2551 South Park \u2551 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick "
u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' "\u2551\n"
u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' "\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' "\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' "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
u'\u2551 South Park \u2551 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
u'\u2551\n' "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d"
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'
) )
assert actual == expected assert actual == expected
# Justify right. # Justify right.
table.justify_columns = {1: 'right'} table.justify_columns = {1: "right"}
actual = table.table actual = table.table
expected = ( expected = (
u'\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550' "\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' "\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' "\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' "\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' "\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' "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n"
"\u2551 Show \u2551 Characters "
u'\u2551 Show \u2551 Characters ' "\u2551\n"
u'\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"
u'\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"
u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' "\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' "\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' "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n"
u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' "\u2551 Rugrats \u2551 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, "
u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n' "\u2551\n"
"\u2551 \u2551 Dil Pickles "
u'\u2551 Rugrats \u2551 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, ' "\u2551\n"
u'\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"
u'\u2551 \u2551 Dil Pickles ' "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
u'\u2551\n' "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
u'\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\u2563\n"
u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' "\u2551 South Park \u2551 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick "
u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' "\u2551\n"
u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' "\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' "\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' "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
u'\u2551 South Park \u2551 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
u'\u2551\n' "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d"
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'
) )
assert actual == expected assert actual == expected
@pytest.mark.skipif(str(not IS_WINDOWS)) @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): def test_windows_screenshot(tmpdir):
"""Test on Windows in a new console window. Take a screenshot to verify it works. """Test on Windows in a new console window. Take a screenshot to verify it works.
:param tmpdir: pytest fixture. :param tmpdir: pytest fixture.
""" """
script = tmpdir.join('script.py') script = tmpdir.join("script.py")
command = [sys.executable, str(script)] command = [sys.executable, str(script)]
screenshot = PROJECT_ROOT.join('test_double_table.png') screenshot = PROJECT_ROOT.join("test_double_table.png")
if screenshot.check(): if screenshot.check():
screenshot.remove() screenshot.remove()
# Generate script. # Generate script.
script_template = dedent(u"""\ script_template = dedent(
"""\
from __future__ import print_function from __future__ import print_function
import os, time import os, time
from colorclass import Color, Windows from colorclass import Color, Windows
from terminaltables import DoubleTable from terminaltables3 import DoubleTable
Windows.enable(auto_colors=True) Windows.enable(auto_colors=True)
stop_after = time.time() + 20 stop_after = time.time() + 20
@ -232,12 +207,13 @@ def test_windows_screenshot(tmpdir):
print('Waiting for screenshot_until_match()...') print('Waiting for screenshot_until_match()...')
while not os.path.exists(r'%s') and time.time() < stop_after: while not os.path.exists(r'%s') and time.time() < stop_after:
time.sleep(0.5) time.sleep(0.5)
""") """
)
script_contents = script_template % str(screenshot) 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. # 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 assert sub_images
# Run. # Run.

View file

@ -1,33 +1,33 @@
"""GithubFlavoredMarkdownTable end to end testing.""" """GithubFlavoredMarkdownTable end to end testing."""
from terminaltables import GithubFlavoredMarkdownTable from terminaltables3 import GithubFlavoredMarkdownTable
def test_single_line(): def test_single_line():
"""Test single-lined cells.""" """Test single-lined cells."""
table_data = [ table_data = [
['Name', 'Color', 'Type'], ["Name", "Color", "Type"],
['Avocado', 'green', 'nut'], ["Avocado", "green", "nut"],
['Tomato', 'red', 'fruit'], ["Tomato", "red", "fruit"],
['Lettuce', 'green', 'vegetable'], ["Lettuce", "green", "vegetable"],
['Watermelon', 'green'], ["Watermelon", "green"],
[], [],
] ]
table = GithubFlavoredMarkdownTable(table_data) table = GithubFlavoredMarkdownTable(table_data)
table.inner_footing_row_border = True table.inner_footing_row_border = True
table.justify_columns[0] = 'left' table.justify_columns[0] = "left"
table.justify_columns[1] = 'center' table.justify_columns[1] = "center"
table.justify_columns[2] = 'right' table.justify_columns[2] = "right"
actual = table.table actual = table.table
expected = ( expected = (
'| Name | Color | Type |\n' "| Name | Color | Type |\n"
'|:-----------|:-----:|----------:|\n' "|:-----------|:-----:|----------:|\n"
'| Avocado | green | nut |\n' "| Avocado | green | nut |\n"
'| Tomato | red | fruit |\n' "| Tomato | red | fruit |\n"
'| Lettuce | green | vegetable |\n' "| Lettuce | green | vegetable |\n"
'| Watermelon | green | |\n' "| Watermelon | green | |\n"
'| | | |' "| | | |"
) )
assert actual == expected assert actual == expected
@ -35,20 +35,23 @@ def test_single_line():
def test_multi_line(): def test_multi_line():
"""Test multi-lined cells.""" """Test multi-lined cells."""
table_data = [ table_data = [
['Show', 'Characters'], ["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'] "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) table = GithubFlavoredMarkdownTable(table_data)
# Test defaults. # Test defaults.
actual = table.table actual = table.table
expected = ( expected = (
'| Show | Characters |\n' "| Show | Characters |\n"
'|------------|-------------------------------------------------------------------------------------|\n' "|------------|-------------------------------------------------------------------------------------|\n"
'| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n' "| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n"
'| | Dil Pickles |\n' "| | Dil Pickles |\n"
'| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |' "| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |"
) )
assert actual == expected assert actual == expected
@ -56,22 +59,22 @@ def test_multi_line():
table.inner_row_border = True table.inner_row_border = True
actual = table.table actual = table.table
expected = ( expected = (
'| Show | Characters |\n' "| Show | Characters |\n"
'|------------|-------------------------------------------------------------------------------------|\n' "|------------|-------------------------------------------------------------------------------------|\n"
'| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n' "| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n"
'| | Dil Pickles |\n' "| | Dil Pickles |\n"
'| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |' "| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |"
) )
assert actual == expected assert actual == expected
# Justify right. # Justify right.
table.justify_columns = {1: 'right'} table.justify_columns = {1: "right"}
actual = table.table actual = table.table
expected = ( expected = (
'| Show | Characters |\n' "| Show | Characters |\n"
'|------------|------------------------------------------------------------------------------------:|\n' "|------------|------------------------------------------------------------------------------------:|\n"
'| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n' "| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n"
'| | Dil Pickles |\n' "| | Dil Pickles |\n"
'| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |' "| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |"
) )
assert actual == expected assert actual == expected

View file

@ -1,29 +1,29 @@
"""PorcelainTable end to end testing.""" """PorcelainTable end to end testing."""
from terminaltables import PorcelainTable from terminaltables3 import PorcelainTable
def test_single_line(): def test_single_line():
"""Test single-lined cells.""" """Test single-lined cells."""
table_data = [ table_data = [
['Name', 'Color', 'Type'], ["Name", "Color", "Type"],
['Avocado', 'green', 'nut'], ["Avocado", "green", "nut"],
['Tomato', 'red', 'fruit'], ["Tomato", "red", "fruit"],
['Lettuce', 'green', 'vegetable'], ["Lettuce", "green", "vegetable"],
['Watermelon', 'green'] ["Watermelon", "green"],
] ]
table = PorcelainTable(table_data) table = PorcelainTable(table_data)
table.justify_columns[0] = 'left' table.justify_columns[0] = "left"
table.justify_columns[1] = 'center' table.justify_columns[1] = "center"
table.justify_columns[2] = 'right' table.justify_columns[2] = "right"
actual = table.table actual = table.table
expected = ( expected = (
' Name | Color | Type \n' " Name | Color | Type \n"
' Avocado | green | nut \n' " Avocado | green | nut \n"
' Tomato | red | fruit \n' " Tomato | red | fruit \n"
' Lettuce | green | vegetable \n' " Lettuce | green | vegetable \n"
' Watermelon | green | ' " Watermelon | green | "
) )
assert actual == expected assert actual == expected
@ -31,29 +31,32 @@ def test_single_line():
def test_multi_line(): def test_multi_line():
"""Test multi-lined cells.""" """Test multi-lined cells."""
table_data = [ table_data = [
['Show', 'Characters'], ["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'] "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) table = PorcelainTable(table_data)
# Test defaults. # Test defaults.
actual = table.table actual = table.table
expected = ( expected = (
' Show | Characters \n' " Show | Characters \n"
' Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, \n' " Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, \n"
' | Dil Pickles \n' " | Dil Pickles \n"
' South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' " South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick "
) )
assert actual == expected assert actual == expected
# Justify right. # Justify right.
table.justify_columns = {1: 'right'} table.justify_columns = {1: "right"}
actual = table.table actual = table.table
expected = ( expected = (
' Show | Characters \n' " Show | Characters \n"
' Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, \n' " Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, \n"
' | Dil Pickles \n' " | Dil Pickles \n"
' South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' " South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick "
) )
assert actual == expected assert actual == expected

View file

@ -2,8 +2,8 @@
import pytest import pytest
from terminaltables import SingleTable from terminaltables3 import SingleTable
from terminaltables.terminal_io import IS_WINDOWS from terminaltables3.terminal_io import IS_WINDOWS
pytestmark = pytest.mark.skipif(str(IS_WINDOWS)) pytestmark = pytest.mark.skipif(str(IS_WINDOWS))
@ -11,44 +11,35 @@ pytestmark = pytest.mark.skipif(str(IS_WINDOWS))
def test_single_line(): def test_single_line():
"""Test single-lined cells.""" """Test single-lined cells."""
table_data = [ table_data = [
['Name', 'Color', 'Type'], ["Name", "Color", "Type"],
['Avocado', 'green', 'nut'], ["Avocado", "green", "nut"],
['Tomato', 'red', 'fruit'], ["Tomato", "red", "fruit"],
['Lettuce', 'green', 'vegetable'], ["Lettuce", "green", "vegetable"],
['Watermelon', 'green'], ["Watermelon", "green"],
[], [],
] ]
table = SingleTable(table_data, 'Example') table = SingleTable(table_data, "Example")
table.inner_footing_row_border = True table.inner_footing_row_border = True
table.justify_columns[0] = 'left' table.justify_columns[0] = "left"
table.justify_columns[1] = 'center' table.justify_columns[1] = "center"
table.justify_columns[2] = 'right' table.justify_columns[2] = "right"
actual = table.table actual = table.table
expected = ( 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' "\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' "\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\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\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' "\033(0\x78\033(B Avocado \033(0\x78\033(B green \033(0\x78\033(B nut \033(0\x78\033(B\n"
'\x71\x71\x71\x71\x71\x71\x71\x75\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 Avocado \033(0\x78\033(B green \033(0\x78\033(B nut \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"
'\033(0\x78\033(B Tomato \033(0\x78\033(B red \033(0\x78\033(B fruit \033(0\x78\033(B\n' "\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\x78\033(B Lettuce \033(0\x78\033(B green \033(0\x78\033(B vegetable \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\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 assert actual == expected
@ -56,41 +47,38 @@ def test_single_line():
def test_multi_line(): def test_multi_line():
"""Test multi-lined cells.""" """Test multi-lined cells."""
table_data = [ table_data = [
['Show', 'Characters'], ["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'] "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) table = SingleTable(table_data)
# Test defaults. # Test defaults.
actual = table.table actual = table.table
expected = ( 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' "\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\x71\x71\x71\x71\x71\x71\x71\x71' "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\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' "\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 Show \033(0\x78\033(B Characters ' " \033(0\x78\033(B\n"
' \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"
'\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"
'\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' "\033(0\x78\033(B Rugrats \033(0\x78\033(B Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille,"
'\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n' " 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 Rugrats \033(0\x78\033(B Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille,' " \033(0\x78\033(B\n"
' Angelica 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\x78\033(B \033(0\x78\033(B Dil Pickles ' "\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"
' \033(0\x78\033(B\n' "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71"
"\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71"
'\033(0\x78\033(B South Park \033(0\x78\033(B Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6a\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\x71\x71\x71\x71\x71'
'\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71'
'\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\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 assert actual == expected
@ -98,74 +86,60 @@ def test_multi_line():
table.inner_row_border = True table.inner_row_border = True
actual = table.table actual = table.table
expected = ( 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' "\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\x71\x71\x71\x71\x71\x71\x71\x71' "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\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' "\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 Show \033(0\x78\033(B Characters ' " \033(0\x78\033(B\n"
' \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"
'\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"
'\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' "\033(0\x78\033(B Rugrats \033(0\x78\033(B Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille,"
'\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n' " 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 Rugrats \033(0\x78\033(B Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille,' " \033(0\x78\033(B\n"
' Angelica 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"
'\033(0\x78\033(B \033(0\x78\033(B Dil Pickles ' "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71"
' \033(0\x78\033(B\n' "\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\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' " \033(0\x78\033(B\n"
'\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' "\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\x75\033(B\n' "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\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\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 assert actual == expected
# Justify right. # Justify right.
table.justify_columns = {1: 'right'} table.justify_columns = {1: "right"}
actual = table.table actual = table.table
expected = ( 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' "\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\x71\x71\x71\x71\x71\x71\x71\x71' "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\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' "\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 "
'\033(0\x78\033(B Show \033(0\x78\033(B ' " Characters \033(0\x78\033(B\n"
' 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"
'\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"
'\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' "\033(0\x78\033(B Rugrats \033(0\x78\033(B Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille,"
'\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n' " Angelica Pickles, \033(0\x78\033(B\n"
"\033(0\x78\033(B \033(0\x78\033(B "
'\033(0\x78\033(B Rugrats \033(0\x78\033(B Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille,' " Dil Pickles \033(0\x78\033(B\n"
' Angelica 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"
'\033(0\x78\033(B \033(0\x78\033(B ' "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71"
' Dil Pickles \033(0\x78\033(B\n' "\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, "
'\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' "Eric Cartman, Kenny McCormick \033(0\x78\033(B\n"
'\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' "\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\x75\033(B\n' "\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\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\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 assert actual == expected

View file

@ -6,8 +6,8 @@ from textwrap import dedent
import py import py
import pytest import pytest
from terminaltables import SingleTable from terminaltables3 import SingleTable
from terminaltables.terminal_io import IS_WINDOWS from terminaltables3.terminal_io import IS_WINDOWS
from tests import PROJECT_ROOT from tests import PROJECT_ROOT
from tests.screenshot import RunNewConsole, screenshot_until_match from tests.screenshot import RunNewConsole, screenshot_until_match
@ -18,44 +18,35 @@ pytestmark = pytest.mark.skipif(str(not IS_WINDOWS))
def test_single_line(): def test_single_line():
"""Test single-lined cells.""" """Test single-lined cells."""
table_data = [ table_data = [
['Name', 'Color', 'Type'], ["Name", "Color", "Type"],
['Avocado', 'green', 'nut'], ["Avocado", "green", "nut"],
['Tomato', 'red', 'fruit'], ["Tomato", "red", "fruit"],
['Lettuce', 'green', 'vegetable'], ["Lettuce", "green", "vegetable"],
['Watermelon', 'green'], ["Watermelon", "green"],
[], [],
] ]
table = SingleTable(table_data, 'Example') table = SingleTable(table_data, "Example")
table.inner_footing_row_border = True table.inner_footing_row_border = True
table.justify_columns[0] = 'left' table.justify_columns[0] = "left"
table.justify_columns[1] = 'center' table.justify_columns[1] = "center"
table.justify_columns[2] = 'right' table.justify_columns[2] = "right"
actual = table.table actual = table.table
expected = ( expected = (
u'\u250cExample\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500' "\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' "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n"
"\u2502 Name \u2502 Color \u2502 Type \u2502\n"
u'\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"
u'\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500' "\u2502 Avocado \u2502 green \u2502 nut \u2502\n"
u'\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n' "\u2502 Tomato \u2502 red \u2502 fruit \u2502\n"
"\u2502 Lettuce \u2502 green \u2502 vegetable \u2502\n"
u'\u2502 Avocado \u2502 green \u2502 nut \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"
u'\u2502 Tomato \u2502 red \u2502 fruit \u2502\n' "\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n"
"\u2502 \u2502 \u2502 \u2502\n"
u'\u2502 Lettuce \u2502 green \u2502 vegetable \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"
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'
) )
assert actual == expected assert actual == expected
@ -63,47 +54,44 @@ def test_single_line():
def test_multi_line(): def test_multi_line():
"""Test multi-lined cells.""" """Test multi-lined cells."""
table_data = [ table_data = [
['Show', 'Characters'], ["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'] "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) table = SingleTable(table_data)
# Test defaults. # Test defaults.
actual = table.table actual = table.table
expected = ( expected = (
u'\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500' "\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' "\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' "\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' "\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' "\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' "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n"
"\u2502 Show \u2502 Characters "
u'\u2502 Show \u2502 Characters ' "\u2502\n"
u'\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"
u'\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"
u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' "\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' "\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' "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n"
u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' "\u2502 Rugrats \u2502 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, "
u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n' "\u2502\n"
"\u2502 \u2502 Dil Pickles "
u'\u2502 Rugrats \u2502 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, ' "\u2502\n"
u'\u2502\n' "\u2502 South Park \u2502 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick "
"\u2502\n"
u'\u2502 \u2502 Dil Pickles ' "\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500"
u'\u2502\n' "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
u'\u2502 South Park \u2502 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
u'\u2502\n' "\u2500\u2500\u2500\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"
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'
) )
assert actual == expected assert actual == expected
@ -111,114 +99,101 @@ def test_multi_line():
table.inner_row_border = True table.inner_row_border = True
actual = table.table actual = table.table
expected = ( expected = (
u'\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500' "\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' "\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' "\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' "\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' "\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' "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n"
"\u2502 Show \u2502 Characters "
u'\u2502 Show \u2502 Characters ' "\u2502\n"
u'\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"
u'\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"
u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' "\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' "\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' "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n"
u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' "\u2502 Rugrats \u2502 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, "
u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n' "\u2502\n"
"\u2502 \u2502 Dil Pickles "
u'\u2502 Rugrats \u2502 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, ' "\u2502\n"
u'\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"
u'\u2502 \u2502 Dil Pickles ' "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
u'\u2502\n' "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
u'\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\u2524\n"
u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' "\u2502 South Park \u2502 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick "
u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' "\u2502\n"
u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' "\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' "\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' "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
u'\u2502 South Park \u2502 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
u'\u2502\n' "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"
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'
) )
assert actual == expected assert actual == expected
# Justify right. # Justify right.
table.justify_columns = {1: 'right'} table.justify_columns = {1: "right"}
actual = table.table actual = table.table
expected = ( expected = (
u'\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500' "\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' "\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' "\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' "\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' "\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' "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n"
"\u2502 Show \u2502 Characters "
u'\u2502 Show \u2502 Characters ' "\u2502\n"
u'\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"
u'\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"
u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' "\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' "\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' "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n"
u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' "\u2502 Rugrats \u2502 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, "
u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n' "\u2502\n"
"\u2502 \u2502 Dil Pickles "
u'\u2502 Rugrats \u2502 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, ' "\u2502\n"
u'\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"
u'\u2502 \u2502 Dil Pickles ' "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
u'\u2502\n' "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
u'\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\u2524\n"
u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' "\u2502 South Park \u2502 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick "
u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' "\u2502\n"
u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' "\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' "\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' "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
u'\u2502 South Park \u2502 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
u'\u2502\n' "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"
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'
) )
assert actual == expected assert actual == expected
@pytest.mark.skipif(str(not IS_WINDOWS)) @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): def test_windows_screenshot(tmpdir):
"""Test on Windows in a new console window. Take a screenshot to verify it works. """Test on Windows in a new console window. Take a screenshot to verify it works.
:param tmpdir: pytest fixture. :param tmpdir: pytest fixture.
""" """
script = tmpdir.join('script.py') script = tmpdir.join("script.py")
command = [sys.executable, str(script)] command = [sys.executable, str(script)]
screenshot = PROJECT_ROOT.join('test_single_table.png') screenshot = PROJECT_ROOT.join("test_single_table.png")
if screenshot.check(): if screenshot.check():
screenshot.remove() screenshot.remove()
# Generate script. # Generate script.
script_template = dedent(u"""\ script_template = dedent(
"""\
from __future__ import print_function from __future__ import print_function
import os, time import os, time
from colorclass import Color, Windows from colorclass import Color, Windows
from terminaltables import SingleTable from terminaltables3 import SingleTable
Windows.enable(auto_colors=True) Windows.enable(auto_colors=True)
stop_after = time.time() + 20 stop_after = time.time() + 20
@ -233,12 +208,13 @@ def test_windows_screenshot(tmpdir):
print('Waiting for screenshot_until_match()...') print('Waiting for screenshot_until_match()...')
while not os.path.exists(r'%s') and time.time() < stop_after: while not os.path.exists(r'%s') and time.time() < stop_after:
time.sleep(0.5) time.sleep(0.5)
""") """
)
script_contents = script_template % str(screenshot) 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. # 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 assert sub_images
# Run. # Run.

View file

@ -2,19 +2,22 @@
import pytest import pytest
from terminaltables.other_tables import AsciiTable from terminaltables3.other_tables import AsciiTable
SINGLE_LINE = ( SINGLE_LINE = (
('Name', 'Color', 'Type'), ("Name", "Color", "Type"),
('Avocado', 'green', 'nut'), ("Avocado", "green", "nut"),
('Tomato', 'red', 'fruit'), ("Tomato", "red", "fruit"),
('Lettuce', 'green', 'vegetable'), ("Lettuce", "green", "vegetable"),
) )
MULTI_LINE = ( MULTI_LINE = (
('Show', 'Characters'), ("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'), "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. :param monkeypatch: pytest fixture.
""" """
monkeypatch.setattr('terminaltables.ascii_table.terminal_size', lambda: (79, 24)) monkeypatch.setattr("terminaltables3.ascii_table.terminal_size", lambda: (79, 24))
monkeypatch.setattr('terminaltables.width_and_alignment.terminal_size', lambda: (79, 24)) monkeypatch.setattr(
"terminaltables3.width_and_alignment.terminal_size", lambda: (79, 24)
)
@pytest.mark.parametrize('table_data,column_number,expected', [ @pytest.mark.parametrize(
([], 0, IndexError), "table_data,column_number,expected",
([[]], 0, IndexError), [
([['']], 1, IndexError), ([], 0, IndexError),
(SINGLE_LINE, 0, 55), ([[]], 0, IndexError),
(SINGLE_LINE, 1, 53), ([[""]], 1, IndexError),
(SINGLE_LINE, 2, 57), (SINGLE_LINE, 0, 55),
(MULTI_LINE, 0, -11), (SINGLE_LINE, 1, 53),
(MULTI_LINE, 1, 62), (SINGLE_LINE, 2, 57),
]) (MULTI_LINE, 0, -11),
(MULTI_LINE, 1, 62),
],
)
def test_column_max_width(table_data, column_number, expected): def test_column_max_width(table_data, column_number, expected):
"""Test method in class. """Test method in class.
@ -47,7 +55,7 @@ def test_column_max_width(table_data, column_number, expected):
""" """
table = AsciiTable(table_data) table = AsciiTable(table_data)
if expected == IndexError: if expected is IndexError:
with pytest.raises(IndexError): with pytest.raises(IndexError):
table.column_max_width(column_number) table.column_max_width(column_number)
return return
@ -58,22 +66,25 @@ def test_column_max_width(table_data, column_number, expected):
def test_column_widths(): def test_column_widths():
"""Test method in class.""" """Test method in class."""
assert AsciiTable([]).column_widths == list() assert AsciiTable([]).column_widths == []
table = AsciiTable(SINGLE_LINE) table = AsciiTable(SINGLE_LINE)
actual = table.column_widths actual = table.column_widths
assert actual == [7, 5, 9] assert actual == [7, 5, 9]
@pytest.mark.parametrize('table_data,terminal_width,expected', [ @pytest.mark.parametrize(
([], None, True), "table_data,terminal_width,expected",
([[]], None, True), [
([['']], None, True), ([], None, True),
(SINGLE_LINE, None, True), ([[]], None, True),
(SINGLE_LINE, 30, False), ([[""]], None, True),
(MULTI_LINE, None, False), (SINGLE_LINE, None, True),
(MULTI_LINE, 100, True), (SINGLE_LINE, 30, False),
]) (MULTI_LINE, None, False),
(MULTI_LINE, 100, True),
],
)
def test_ok(monkeypatch, table_data, terminal_width, expected): def test_ok(monkeypatch, table_data, terminal_width, expected):
"""Test method in class. """Test method in class.
@ -83,20 +94,25 @@ def test_ok(monkeypatch, table_data, terminal_width, expected):
:param bool expected: Expected return value. :param bool expected: Expected return value.
""" """
if terminal_width is not None: 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) table = AsciiTable(table_data)
actual = table.ok actual = table.ok
assert actual is expected assert actual is expected
@pytest.mark.parametrize('table_data,expected', [ @pytest.mark.parametrize(
([], 2), "table_data,expected",
([[]], 2), [
([['']], 4), ([], 2),
([[' ']], 5), ([[]], 2),
(SINGLE_LINE, 31), ([[""]], 4),
(MULTI_LINE, 100), ([[" "]], 5),
]) (SINGLE_LINE, 31),
(MULTI_LINE, 100),
],
)
def test_table_width(table_data, expected): def test_table_width(table_data, expected):
"""Test method in class. """Test method in class.

View file

View file

@ -2,47 +2,47 @@
import pytest 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): def test_single_line(style):
"""Test with single-line row. """Test with single-line row.
:param str style: Passed to method. :param str style: Passed to method.
""" """
row = ['Row One Column One', 'Two', 'Three'] row = ["Row One Column One", "Two", "Three"]
table = BaseTable([row]) table = BaseTable([row])
actual = [tuple(i) for i in table.gen_row_lines(row, style, [18, 3, 5], 1)] actual = [tuple(i) for i in table.gen_row_lines(row, style, [18, 3, 5], 1)]
expected = [ expected = [
('|', ' Row One Column One ', '|', ' Two ', '|', ' Three ', '|'), ("|", " Row One Column One ", "|", " Two ", "|", " Three ", "|"),
] ]
assert actual == expected assert actual == expected
@pytest.mark.parametrize('style', ['heading', 'footing', 'row']) @pytest.mark.parametrize("style", ["heading", "footing", "row"])
def test_multi_line(style): def test_multi_line(style):
"""Test with multi-line row. """Test with multi-line row.
:param str style: Passed to method. :param str style: Passed to method.
""" """
row = ['Row One\nColumn One', 'Two', 'Three'] row = ["Row One\nColumn One", "Two", "Three"]
table = BaseTable([row]) table = BaseTable([row])
actual = [tuple(i) for i in table.gen_row_lines(row, style, [10, 3, 5], 2)] actual = [tuple(i) for i in table.gen_row_lines(row, style, [10, 3, 5], 2)]
expected = [ expected = [
('|', ' Row One ', '|', ' Two ', '|', ' Three ', '|'), ("|", " Row One ", "|", " Two ", "|", " Three ", "|"),
('|', ' Column One ', '|', ' ', '|', ' ', '|'), ("|", " Column One ", "|", " ", "|", " ", "|"),
] ]
assert actual == expected assert actual == expected
@pytest.mark.parametrize('style', ['heading', 'footing', 'row']) @pytest.mark.parametrize("style", ["heading", "footing", "row"])
def test_no_padding_no_borders(style): def test_no_padding_no_borders(style):
"""Test without padding or borders. """Test without padding or borders.
:param str style: Passed to method. :param str style: Passed to method.
""" """
row = ['Row One\nColumn One', 'Two', 'Three'] row = ["Row One\nColumn One", "Two", "Three"]
table = BaseTable([row]) table = BaseTable([row])
table.inner_column_border = False table.inner_column_border = False
table.outer_border = False table.outer_border = False
@ -50,28 +50,28 @@ def test_no_padding_no_borders(style):
table.padding_right = 0 table.padding_right = 0
actual = [tuple(i) for i in table.gen_row_lines(row, style, [10, 3, 5], 2)] actual = [tuple(i) for i in table.gen_row_lines(row, style, [10, 3, 5], 2)]
expected = [ expected = [
('Row One ', 'Two', 'Three'), ("Row One ", "Two", "Three"),
('Column One', ' ', ' '), ("Column One", " ", " "),
] ]
assert actual == expected assert actual == expected
@pytest.mark.parametrize('style', ['heading', 'footing', 'row']) @pytest.mark.parametrize("style", ["heading", "footing", "row"])
def test_uneven(style): def test_uneven(style):
"""Test with row missing cells. """Test with row missing cells.
:param str style: Passed to method. :param str style: Passed to method.
""" """
row = ['Row One Column One'] row = ["Row One Column One"]
table = BaseTable([row]) table = BaseTable([row])
actual = [tuple(i) for i in table.gen_row_lines(row, style, [18, 3, 5], 1)] actual = [tuple(i) for i in table.gen_row_lines(row, style, [18, 3, 5], 1)]
expected = [ expected = [
('|', ' Row One Column One ', '|', ' ', '|', ' ', '|'), ("|", " Row One Column One ", "|", " ", "|", " ", "|"),
] ]
assert actual == expected assert actual == expected
@pytest.mark.parametrize('style', ['heading', 'footing', 'row']) @pytest.mark.parametrize("style", ["heading", "footing", "row"])
def test_empty_table(style): def test_empty_table(style):
"""Test empty table. """Test empty table.
@ -81,6 +81,6 @@ def test_empty_table(style):
table = BaseTable([row]) table = BaseTable([row])
actual = [tuple(i) for i in table.gen_row_lines(row, style, [], 0)] actual = [tuple(i) for i in table.gen_row_lines(row, style, [], 0)]
expected = [ expected = [
('|', '|'), ("|", "|"),
] ]
assert actual == expected assert actual == expected

View file

@ -2,15 +2,17 @@
import pytest import pytest
from terminaltables.base_table import BaseTable from terminaltables3.base_table import BaseTable
from terminaltables.build import flatten from terminaltables3.build import flatten
from terminaltables.width_and_alignment import max_dimensions from terminaltables3.width_and_alignment import max_dimensions
@pytest.mark.parametrize('inner_heading_row_border', [True, False]) @pytest.mark.parametrize("inner_heading_row_border", [True, False])
@pytest.mark.parametrize('inner_footing_row_border', [True, False]) @pytest.mark.parametrize("inner_footing_row_border", [True, False])
@pytest.mark.parametrize('inner_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): def test_inner_row_borders(
inner_heading_row_border, inner_footing_row_border, inner_row_border
):
"""Test heading/footing/row borders. """Test heading/footing/row borders.
:param bool inner_heading_row_border: Passed to table. :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. :param bool inner_row_border: Passed to table.
""" """
table_data = [ table_data = [
['Name', 'Color', 'Type'], ["Name", "Color", "Type"],
['Avocado', 'green', 'nut'], ["Avocado", "green", "nut"],
['Tomato', 'red', 'fruit'], ["Tomato", "red", "fruit"],
['Lettuce', 'green', 'vegetable'], ["Lettuce", "green", "vegetable"],
] ]
table = BaseTable(table_data) table = BaseTable(table_data)
table.inner_heading_row_border = inner_heading_row_border table.inner_heading_row_border = inner_heading_row_border
table.inner_footing_row_border = inner_footing_row_border table.inner_footing_row_border = inner_footing_row_border
table.inner_row_border = inner_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)) actual = flatten(table.gen_table(inner_widths, inner_heights, outer_widths))
# Determine expected. # Determine expected.
if inner_row_border: if inner_row_border:
expected = ( expected = (
'+---------+-------+-----------+\n' "+---------+-------+-----------+\n"
'| Name | Color | Type |\n' "| Name | Color | Type |\n"
'+---------+-------+-----------+\n' "+---------+-------+-----------+\n"
'| Avocado | green | nut |\n' "| Avocado | green | nut |\n"
'+---------+-------+-----------+\n' "+---------+-------+-----------+\n"
'| Tomato | red | fruit |\n' "| Tomato | red | fruit |\n"
'+---------+-------+-----------+\n' "+---------+-------+-----------+\n"
'| Lettuce | green | vegetable |\n' "| Lettuce | green | vegetable |\n"
'+---------+-------+-----------+' "+---------+-------+-----------+"
) )
elif inner_heading_row_border and inner_footing_row_border: elif inner_heading_row_border and inner_footing_row_border:
expected = ( expected = (
'+---------+-------+-----------+\n' "+---------+-------+-----------+\n"
'| Name | Color | Type |\n' "| Name | Color | Type |\n"
'+---------+-------+-----------+\n' "+---------+-------+-----------+\n"
'| Avocado | green | nut |\n' "| Avocado | green | nut |\n"
'| Tomato | red | fruit |\n' "| Tomato | red | fruit |\n"
'+---------+-------+-----------+\n' "+---------+-------+-----------+\n"
'| Lettuce | green | vegetable |\n' "| Lettuce | green | vegetable |\n"
'+---------+-------+-----------+' "+---------+-------+-----------+"
) )
elif inner_heading_row_border: elif inner_heading_row_border:
expected = ( expected = (
'+---------+-------+-----------+\n' "+---------+-------+-----------+\n"
'| Name | Color | Type |\n' "| Name | Color | Type |\n"
'+---------+-------+-----------+\n' "+---------+-------+-----------+\n"
'| Avocado | green | nut |\n' "| Avocado | green | nut |\n"
'| Tomato | red | fruit |\n' "| Tomato | red | fruit |\n"
'| Lettuce | green | vegetable |\n' "| Lettuce | green | vegetable |\n"
'+---------+-------+-----------+' "+---------+-------+-----------+"
) )
elif inner_footing_row_border: elif inner_footing_row_border:
expected = ( expected = (
'+---------+-------+-----------+\n' "+---------+-------+-----------+\n"
'| Name | Color | Type |\n' "| Name | Color | Type |\n"
'| Avocado | green | nut |\n' "| Avocado | green | nut |\n"
'| Tomato | red | fruit |\n' "| Tomato | red | fruit |\n"
'+---------+-------+-----------+\n' "+---------+-------+-----------+\n"
'| Lettuce | green | vegetable |\n' "| Lettuce | green | vegetable |\n"
'+---------+-------+-----------+' "+---------+-------+-----------+"
) )
else: else:
expected = ( expected = (
'+---------+-------+-----------+\n' "+---------+-------+-----------+\n"
'| Name | Color | Type |\n' "| Name | Color | Type |\n"
'| Avocado | green | nut |\n' "| Avocado | green | nut |\n"
'| Tomato | red | fruit |\n' "| Tomato | red | fruit |\n"
'| Lettuce | green | vegetable |\n' "| Lettuce | green | vegetable |\n"
'+---------+-------+-----------+' "+---------+-------+-----------+"
) )
assert actual == expected assert actual == expected
@pytest.mark.parametrize('outer_border', [True, False]) @pytest.mark.parametrize("outer_border", [True, False])
def test_outer_borders(outer_border): def test_outer_borders(outer_border):
"""Test left/right/top/bottom table borders. """Test left/right/top/bottom table borders.
:param bool outer_border: Passed to table. :param bool outer_border: Passed to table.
""" """
table_data = [ table_data = [
['Name', 'Color', 'Type'], ["Name", "Color", "Type"],
['Avocado', 'green', 'nut'], ["Avocado", "green", "nut"],
['Tomato', 'red', 'fruit'], ["Tomato", "red", "fruit"],
['Lettuce', 'green', 'vegetable'], ["Lettuce", "green", "vegetable"],
] ]
table = BaseTable(table_data, 'Example Table') table = BaseTable(table_data, "Example Table")
table.outer_border = outer_border 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)) actual = flatten(table.gen_table(inner_widths, inner_heights, outer_widths))
# Determine expected. # Determine expected.
if outer_border: if outer_border:
expected = ( expected = (
'+Example Table----+-----------+\n' "+Example Table----+-----------+\n"
'| Name | Color | Type |\n' "| Name | Color | Type |\n"
'+---------+-------+-----------+\n' "+---------+-------+-----------+\n"
'| Avocado | green | nut |\n' "| Avocado | green | nut |\n"
'| Tomato | red | fruit |\n' "| Tomato | red | fruit |\n"
'| Lettuce | green | vegetable |\n' "| Lettuce | green | vegetable |\n"
'+---------+-------+-----------+' "+---------+-------+-----------+"
) )
else: else:
expected = ( expected = (
' Name | Color | Type \n' " Name | Color | Type \n"
'---------+-------+-----------\n' "---------+-------+-----------\n"
' Avocado | green | nut \n' " Avocado | green | nut \n"
' Tomato | red | fruit \n' " Tomato | red | fruit \n"
' Lettuce | green | vegetable ' " Lettuce | green | vegetable "
) )
assert actual == expected assert actual == expected
@pytest.mark.parametrize('mode', ['row', 'one', 'blank', 'empty', 'none']) @pytest.mark.parametrize("mode", ["row", "one", "blank", "empty", "none"])
@pytest.mark.parametrize('bare', [False, True]) @pytest.mark.parametrize("bare", [False, True])
def test_one_no_rows(mode, bare): def test_one_no_rows(mode, bare):
"""Test with one or no rows. """Test with one or no rows.
:param str mode: Type of table contents to test. :param str mode: Type of table contents to test.
:param bool bare: Disable padding/borders. :param bool bare: Disable padding/borders.
""" """
if mode == 'row': if mode == "row":
table_data = [ table_data = [
['Avocado', 'green', 'nut'], ["Avocado", "green", "nut"],
] ]
elif mode == 'one': elif mode == "one":
table_data = [ table_data = [
['Avocado'], ["Avocado"],
] ]
elif mode == 'blank': elif mode == "blank":
table_data = [ table_data = [
[''], [""],
] ]
elif mode == 'empty': elif mode == "empty":
table_data = [ table_data = [
[], [],
] ]
else: else:
table_data = [ table_data = []
]
table = BaseTable(table_data) table = BaseTable(table_data)
if bare: if bare:
table.inner_column_border = False table.inner_column_border = False
@ -163,63 +168,40 @@ def test_one_no_rows(mode, bare):
table.outer_border = False table.outer_border = False
table.padding_left = 0 table.padding_left = 0
table.padding_right = 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)) actual = flatten(table.gen_table(inner_widths, inner_heights, outer_widths))
# Determine expected. # Determine expected.
if mode == 'row': if mode == "row":
if bare: if bare:
expected = ( expected = "Avocadogreennut"
'Avocadogreennut'
)
else: else:
expected = ( expected = (
'+---------+-------+-----+\n' "+---------+-------+-----+\n"
'| Avocado | green | nut |\n' "| Avocado | green | nut |\n"
'+---------+-------+-----+' "+---------+-------+-----+"
) )
elif mode == 'one': elif mode == "one":
if bare: if bare:
expected = ( expected = "Avocado"
'Avocado'
)
else: else:
expected = ( expected = "+---------+\n" "| Avocado |\n" "+---------+"
'+---------+\n' elif mode == "blank": # Remember there's still padding.
'| Avocado |\n'
'+---------+'
)
elif mode == 'blank': # Remember there's still padding.
if bare: if bare:
expected = ( expected = ""
''
)
else: else:
expected = ( expected = "+--+\n" "| |\n" "+--+"
'+--+\n' elif mode == "empty":
'| |\n'
'+--+'
)
elif mode == 'empty':
if bare: if bare:
expected = ( expected = ""
''
)
else: else:
expected = ( expected = "++\n" "||\n" "++"
'++\n'
'||\n'
'++'
)
else: else:
if bare: if bare:
expected = ( expected = ""
''
)
else: else:
expected = ( expected = "++\n" "++"
'++\n'
'++'
)
assert actual == expected assert actual == expected

View file

@ -2,47 +2,49 @@
import pytest import pytest
from terminaltables.base_table import BaseTable from terminaltables3.base_table import BaseTable
from terminaltables.width_and_alignment import max_dimensions from terminaltables3.width_and_alignment import max_dimensions
SINGLE_LINE = ( SINGLE_LINE = (
('Name', 'Color', 'Type'), ("Name", "Color", "Type"),
('Avocado', 'green', 'nut'), ("Avocado", "green", "nut"),
('Tomato', 'red', 'fruit'), ("Tomato", "red", "fruit"),
('Lettuce', 'green', 'vegetable'), ("Lettuce", "green", "vegetable"),
) )
@pytest.mark.parametrize('inner_column_border', [True, False]) @pytest.mark.parametrize("inner_column_border", [True, False])
@pytest.mark.parametrize('style', ['top', 'bottom']) @pytest.mark.parametrize("style", ["top", "bottom"])
def test_top_bottom(inner_column_border, style): def test_top_bottom(inner_column_border, style):
"""Test top and bottom borders. """Test top and bottom borders.
:param bool inner_column_border: Passed to table class. :param bool inner_column_border: Passed to table class.
:param str style: Passed to method. :param str style: Passed to method.
""" """
table = BaseTable(SINGLE_LINE, 'Example') table = BaseTable(SINGLE_LINE, "Example")
table.inner_column_border = inner_column_border 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. # Determine expected.
if style == 'top' and inner_column_border: if style == "top" and inner_column_border:
expected = '+Example--+-------+-----------+' expected = "+Example--+-------+-----------+"
elif style == 'top': elif style == "top":
expected = '+Example--------------------+' expected = "+Example--------------------+"
elif style == 'bottom' and inner_column_border: elif style == "bottom" and inner_column_border:
expected = '+---------+-------+-----------+' expected = "+---------+-------+-----------+"
else: else:
expected = '+---------------------------+' expected = "+---------------------------+"
# Test. # Test.
actual = ''.join(table.horizontal_border(style, outer_widths)) actual = "".join(table.horizontal_border(style, outer_widths))
assert actual == expected assert actual == expected
@pytest.mark.parametrize('inner_column_border', [True, False]) @pytest.mark.parametrize("inner_column_border", [True, False])
@pytest.mark.parametrize('outer_border', [True, False]) @pytest.mark.parametrize("outer_border", [True, False])
@pytest.mark.parametrize('style', ['heading', 'footing']) @pytest.mark.parametrize("style", ["heading", "footing"])
def test_heading_footing(inner_column_border, outer_border, style): def test_heading_footing(inner_column_border, outer_border, style):
"""Test heading and footing borders. """Test heading and footing borders.
@ -53,25 +55,43 @@ def test_heading_footing(inner_column_border, outer_border, style):
table = BaseTable(SINGLE_LINE) table = BaseTable(SINGLE_LINE)
table.inner_column_border = inner_column_border table.inner_column_border = inner_column_border
table.outer_border = outer_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. # Determine expected.
if style == 'heading' and outer_border: if style == "heading" and outer_border:
expected = '+---------+-------+-----------+' if inner_column_border else '+---------------------------+' expected = (
elif style == 'heading': "+---------+-------+-----------+"
expected = '---------+-------+-----------' if inner_column_border else '---------------------------' if inner_column_border
elif style == 'footing' and outer_border: else "+---------------------------+"
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: else:
expected = '---------+-------+-----------' if inner_column_border else '---------------------------' expected = (
"---------+-------+-----------"
if inner_column_border
else "---------------------------"
)
# Test. # Test.
actual = ''.join(table.horizontal_border(style, outer_widths)) actual = "".join(table.horizontal_border(style, outer_widths))
assert actual == expected assert actual == expected
@pytest.mark.parametrize('inner_column_border', [True, False]) @pytest.mark.parametrize("inner_column_border", [True, False])
@pytest.mark.parametrize('outer_border', [True, False]) @pytest.mark.parametrize("outer_border", [True, False])
def test_row(inner_column_border, outer_border): def test_row(inner_column_border, outer_border):
"""Test inner borders. """Test inner borders.
@ -81,18 +101,20 @@ def test_row(inner_column_border, outer_border):
table = BaseTable(SINGLE_LINE) table = BaseTable(SINGLE_LINE)
table.inner_column_border = inner_column_border table.inner_column_border = inner_column_border
table.outer_border = outer_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. # Determine expected.
if inner_column_border and outer_border: if inner_column_border and outer_border:
expected = '+---------+-------+-----------+' expected = "+---------+-------+-----------+"
elif inner_column_border: elif inner_column_border:
expected = '---------+-------+-----------' expected = "---------+-------+-----------"
elif outer_border: elif outer_border:
expected = '+---------------------------+' expected = "+---------------------------+"
else: else:
expected = '---------------------------' expected = "---------------------------"
# Test. # Test.
actual = ''.join(table.horizontal_border('row', outer_widths)) actual = "".join(table.horizontal_border("row", outer_widths))
assert actual == expected assert actual == expected

View file

@ -1,32 +1,31 @@
# coding: utf-8
"""Test property in BaseTable class.""" """Test property in BaseTable class."""
from colorama import Fore from colorama import Fore
from colorclass import Color from colorclass import Color
from termcolor import colored from termcolor import colored
from terminaltables.base_table import BaseTable from terminaltables3.base_table import BaseTable
def test_ascii(): def test_ascii():
"""Test with ASCII characters.""" """Test with ASCII characters."""
table_data = [ table_data = [
['Name', 'Color', 'Type'], ["Name", "Color", "Type"],
['Avocado', 'green', 'nut'], ["Avocado", "green", "nut"],
['Tomato', 'red', 'fruit'], ["Tomato", "red", "fruit"],
['Lettuce', 'green', 'vegetable'], ["Lettuce", "green", "vegetable"],
] ]
table = BaseTable(table_data) table = BaseTable(table_data)
actual = table.table actual = table.table
expected = ( expected = (
'+---------+-------+-----------+\n' "+---------+-------+-----------+\n"
'| Name | Color | Type |\n' "| Name | Color | Type |\n"
'+---------+-------+-----------+\n' "+---------+-------+-----------+\n"
'| Avocado | green | nut |\n' "| Avocado | green | nut |\n"
'| Tomato | red | fruit |\n' "| Tomato | red | fruit |\n"
'| Lettuce | green | vegetable |\n' "| Lettuce | green | vegetable |\n"
'+---------+-------+-----------+' "+---------+-------+-----------+"
) )
assert actual == expected assert actual == expected
@ -44,13 +43,13 @@ def test_int():
actual = table.table actual = table.table
expected = ( expected = (
'+1234567890+---+\n' "+1234567890+---+\n"
'| 100 | 10 | 1 |\n' "| 100 | 10 | 1 |\n"
'+-----+----+---+\n' "+-----+----+---+\n"
'| 0 | 3 | 6 |\n' "| 0 | 3 | 6 |\n"
'| 1 | 4 | 7 |\n' "| 1 | 4 | 7 |\n"
'| 2 | 5 | 8 |\n' "| 2 | 5 | 8 |\n"
'+-----+----+---+' "+-----+----+---+"
) )
assert actual == expected assert actual == expected
@ -68,13 +67,13 @@ def test_float():
actual = table.table actual = table.table
expected = ( expected = (
'+0.12345678--+-------+\n' "+0.12345678--+-------+\n"
'| 1.0 | 22.0 | 333.0 |\n' "| 1.0 | 22.0 | 333.0 |\n"
'+-----+------+-------+\n' "+-----+------+-------+\n"
'| 0.1 | 3.1 | 6.1 |\n' "| 0.1 | 3.1 | 6.1 |\n"
'| 1.1 | 4.1 | 7.1 |\n' "| 1.1 | 4.1 | 7.1 |\n"
'| 2.1 | 5.1 | 8.1 |\n' "| 2.1 | 5.1 | 8.1 |\n"
'+-----+------+-------+' "+-----+------+-------+"
) )
assert actual == expected assert actual == expected
@ -92,13 +91,13 @@ def test_bool_none():
actual = table.table actual = table.table
expected = ( expected = (
'+True---+-------+-------+\n' "+True---+-------+-------+\n"
'| True | False | None |\n' "| True | False | None |\n"
'+-------+-------+-------+\n' "+-------+-------+-------+\n"
'| True | False | None |\n' "| True | False | None |\n"
'| False | None | True |\n' "| False | None | True |\n"
'| None | True | False |\n' "| None | True | False |\n"
'+-------+-------+-------+' "+-------+-------+-------+"
) )
assert actual == expected assert actual == expected
@ -107,20 +106,20 @@ def test_bool_none():
def test_cjk(): def test_cjk():
"""Test with CJK characters.""" """Test with CJK characters."""
table_data = [ table_data = [
['CJK'], ["CJK"],
['蓝色'], ["蓝色"],
['世界你好'], ["世界你好"],
] ]
table = BaseTable(table_data) table = BaseTable(table_data)
actual = table.table actual = table.table
expected = ( expected = (
'+----------+\n' "+----------+\n"
'| CJK |\n' "| CJK |\n"
'+----------+\n' "+----------+\n"
'| 蓝色 |\n' "| 蓝色 |\n"
'| 世界你好 |\n' "| 世界你好 |\n"
'+----------+' "+----------+"
) )
assert actual == expected assert actual == expected
@ -129,20 +128,15 @@ def test_cjk():
def test_rtl(): def test_rtl():
"""Test with RTL characters.""" """Test with RTL characters."""
table_data = [ table_data = [
['RTL'], ["RTL"],
['שלום'], ["שלום"],
['معرب'], ["معرب"],
] ]
table = BaseTable(table_data) table = BaseTable(table_data)
actual = table.table actual = table.table
expected = ( expected = (
'+------+\n' "+------+\n" "| RTL |\n" "+------+\n" "| שלום |\n" "| معرب |\n" "+------+"
'| RTL |\n'
'+------+\n'
'| שלום |\n'
'| معرب |\n'
'+------+'
) )
assert actual == expected assert actual == expected
@ -151,22 +145,22 @@ def test_rtl():
def test_rtl_large(): def test_rtl_large():
"""Test large table of RTL characters.""" """Test large table of RTL characters."""
table_data = [ table_data = [
['اكتب', 'اللون', 'اسم'], ["اكتب", "اللون", "اسم"],
['البندق', 'أخضر', 'أفوكادو'], ["البندق", "أخضر", "أفوكادو"],
['ثمرة', 'أحمر', 'بندورة'], ["ثمرة", "أحمر", "بندورة"],
['الخضروات', 'أخضر', 'الخس'], ["الخضروات", "أخضر", "الخس"],
] ]
table = BaseTable(table_data, 'جوجل المترجم') table = BaseTable(table_data, "جوجل المترجم")
actual = table.table actual = table.table
expected = ( expected = (
'+جوجل المترجم------+---------+\n' "+جوجل المترجم------+---------+\n"
'| اكتب | اللون | اسم |\n' "| اكتب | اللون | اسم |\n"
'+----------+-------+---------+\n' "+----------+-------+---------+\n"
'| البندق | أخضر | أفوكادو |\n' "| البندق | أخضر | أفوكادو |\n"
'| ثمرة | أحمر | بندورة |\n' "| ثمرة | أحمر | بندورة |\n"
'| الخضروات | أخضر | الخس |\n' "| الخضروات | أخضر | الخس |\n"
'+----------+-------+---------+' "+----------+-------+---------+"
) )
assert actual == expected assert actual == expected
@ -175,22 +169,42 @@ def test_rtl_large():
def test_color(): def test_color():
"""Test with color characters.""" """Test with color characters."""
table_data = [ 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}')], "ansi",
['colorama', Fore.RED + 'Red' + Fore.RESET, Fore.GREEN + 'Green' + Fore.RESET, Fore.BLUE + 'Blue' + Fore.RESET], "\033[31mRed\033[39m",
['termcolor', colored('Red', 'red'), colored('Green', 'green'), colored('Blue', 'blue')], "\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 = BaseTable(table_data)
table.inner_heading_row_border = False table.inner_heading_row_border = False
actual = table.table actual = table.table
expected = ( expected = (
u'+------------+-----+-------+------+\n' "+------------+-----+-------+------+\n"
u'| ansi | \033[31mRed\033[39m | \033[32mGreen\033[39m | \033[34mBlue\033[39m |\n' "| 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' "| 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' "| 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' "| termcolor | \033[31mRed\033[0m | \033[32mGreen\033[0m | \033[34mBlue\033[0m |\n"
u'+------------+-----+-------+------+' "+------------+-----+-------+------+"
) )
assert actual == expected assert actual == expected

View file

View file

@ -1,4 +1,3 @@
# coding: utf-8
"""Test function in module.""" """Test function in module."""
import pytest import pytest
@ -6,16 +5,19 @@ from colorama import Fore, Style
from colorclass import Color from colorclass import Color
from termcolor import colored 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', [ @pytest.mark.parametrize(
([5, 6, 7], '-', '<', '+', '>', '<-----+------+------->'), "outer_widths,horizontal,left,intersect,right,expected",
([1, 1, 1], '-', '', '', '', '---'), [
([1, 1, 1], '', '', '', '', ''), ([5, 6, 7], "-", "<", "+", ">", "<-----+------+------->"),
([1], '-', '<', '+', '>', '<->'), ([1, 1, 1], "-", "", "", "", "---"),
([], '-', '<', '+', '>', '<>'), ([1, 1, 1], "", "", "", "", ""),
]) ([1], "-", "<", "+", ">", "<->"),
([], "-", "<", "+", ">", "<>"),
],
)
def test_no_title(outer_widths, horizontal, left, intersect, right, expected): def test_no_title(outer_widths, horizontal, left, intersect, right, expected):
"""Test without title. """Test without title.
@ -27,26 +29,25 @@ def test_no_title(outer_widths, horizontal, left, intersect, right, expected):
:param str expected: Expected output. :param str expected: Expected output.
""" """
actual = build_border(outer_widths, horizontal, left, intersect, right) actual = build_border(outer_widths, horizontal, left, intersect, right)
assert ''.join(actual) == expected assert "".join(actual) == expected
@pytest.mark.parametrize('outer_widths,intersect,expected', [ @pytest.mark.parametrize(
([20], '+', 'Applications--------'), "outer_widths,intersect,expected",
([20], '', 'Applications--------'), [
([20], "+", "Applications--------"),
([15, 5], '+', 'Applications---+-----'), ([20], "", "Applications--------"),
([15, 5], '', 'Applications--------'), ([15, 5], "+", "Applications---+-----"),
([15, 5], "", "Applications--------"),
([12], '+', 'Applications'), ([12], "+", "Applications"),
([12], '', 'Applications'), ([12], "", "Applications"),
([12, 1], "+", "Applications+-"),
([12, 1], '+', 'Applications+-'), ([12, 1], "", "Applications-"),
([12, 1], '', 'Applications-'), ([12, 0], "+", "Applications+"),
([12, 0], "", "Applications"),
([12, 0], '+', 'Applications+'), ],
([12, 0], '', 'Applications'), )
]) @pytest.mark.parametrize("left,right", [("", ""), ("<", ">")])
@pytest.mark.parametrize('left,right', [('', ''), ('<', '>')])
def test_first_column_fit(outer_widths, left, intersect, right, expected): def test_first_column_fit(outer_widths, left, intersect, right, expected):
"""Test with title that fits in the first column. """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: if left and right:
expected = left + expected + right expected = left + expected + right
actual = build_border(outer_widths, '-', left, intersect, right, title='Applications') actual = build_border(
assert ''.join(actual) == expected outer_widths, "-", left, intersect, right, title="Applications"
)
assert "".join(actual) == expected
@pytest.mark.parametrize('outer_widths,expected', [ @pytest.mark.parametrize(
([20], 'Applications--------'), "outer_widths,expected",
([10, 10], 'Applications--------'), [
([5, 5, 5, 5], 'Applications--------'), ([20], "Applications--------"),
([3, 2, 3, 2, 3, 2, 3, 2], 'Applications--------'), ([10, 10], "Applications--------"),
([1] * 20, 'Applications--------'), ([5, 5, 5, 5], "Applications--------"),
([10, 5], 'Applications---'), ([3, 2, 3, 2, 3, 2, 3, 2], "Applications--------"),
([9, 5], 'Applications--'), ([1] * 20, "Applications--------"),
([8, 5], 'Applications-'), ([10, 5], "Applications---"),
([7, 5], 'Applications'), ([9, 5], "Applications--"),
([6, 5], '-----------'), ([8, 5], "Applications-"),
]) ([7, 5], "Applications"),
@pytest.mark.parametrize('left,right', [('', ''), ('<', '>')]) ([6, 5], "-----------"),
],
)
@pytest.mark.parametrize("left,right", [("", ""), ("<", ">")])
def test_no_intersect(outer_widths, left, right, expected): def test_no_intersect(outer_widths, left, right, expected):
"""Test with no column dividers. """Test with no column dividers.
@ -85,37 +91,39 @@ def test_no_intersect(outer_widths, left, right, expected):
""" """
if left and right: if left and right:
expected = left + expected + right expected = left + expected + right
actual = build_border(outer_widths, '-', left, '', right, title='Applications') actual = build_border(outer_widths, "-", left, "", right, title="Applications")
assert ''.join(actual) == expected assert "".join(actual) == expected
@pytest.mark.parametrize('outer_widths,expected', [ @pytest.mark.parametrize(
([20], 'Applications--------'), "outer_widths,expected",
([0, 20], 'Applications---------'), [
([20, 0], 'Applications--------+'), ([20], "Applications--------"),
([0, 0, 20], 'Applications----------'), ([0, 20], "Applications---------"),
([20, 0, 0], 'Applications--------++'), ([20, 0], "Applications--------+"),
([0, 0, 20], "Applications----------"),
([10, 10], 'Applications---------'), ([20, 0, 0], "Applications--------++"),
([11, 9], 'Applications---------'), ([10, 10], "Applications---------"),
([12, 8], 'Applications+--------'), ([11, 9], "Applications---------"),
([13, 7], 'Applications-+-------'), ([12, 8], "Applications+--------"),
([13, 7], "Applications-+-------"),
([5, 5, 5, 5], 'Applications-----+-----'), ([5, 5, 5, 5], "Applications-----+-----"),
([4, 4, 6, 6], 'Applications----+------'), ([4, 4, 6, 6], "Applications----+------"),
([3, 3, 7, 7], 'Applications---+-------'), ([3, 3, 7, 7], "Applications---+-------"),
([2, 2, 7, 9], 'Applications-+---------'), ([2, 2, 7, 9], "Applications-+---------"),
([1, 1, 9, 9], 'Applications-+---------'), ([1, 1, 9, 9], "Applications-+---------"),
([2, 2, 2, 2, 2, 2, 2], "Applications--+--+--"),
([2, 2, 2, 2, 2, 2, 2], 'Applications--+--+--'), ([1, 1, 1, 1, 1, 1, 1, 1, 1, 1], "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++++++++'), [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], '-+-+-+-+-'), ([2, 2, 2, 2], "--+--+--+--"),
([0, 0, 0, 0, 0, 0, 0, 0, 0, 0], '+++++++++'), ([1, 1, 1, 1, 1], "-+-+-+-+-"),
]) ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "+++++++++"),
@pytest.mark.parametrize('left,right', [('', ''), ('<', '>')]) ],
)
@pytest.mark.parametrize("left,right", [("", ""), ("<", ">")])
def test_intersect(outer_widths, left, right, expected): def test_intersect(outer_widths, left, right, expected):
"""Test with column dividers. """Test with column dividers.
@ -126,48 +134,51 @@ def test_intersect(outer_widths, left, right, expected):
""" """
if left and right: if left and right:
expected = left + expected + right expected = left + expected + right
actual = build_border(outer_widths, '-', left, '+', right, title='Applications') actual = build_border(outer_widths, "-", left, "+", right, title="Applications")
assert ''.join(actual) == expected assert "".join(actual) == expected
@pytest.mark.parametrize('outer_widths,intersect,expected', [ @pytest.mark.parametrize(
([12], '+', u'蓝色--------'), "outer_widths,intersect,expected",
([12], '', u'蓝色--------'), [
([7, 5], '+', u'蓝色---+-----'), ([12], "+", "蓝色--------"),
([7, 5], '', u'蓝色--------'), ([12], "", "蓝色--------"),
([4], '+', u'蓝色'), ([7, 5], "+", "蓝色---+-----"),
([4], '', u'蓝色'), ([7, 5], "", "蓝色--------"),
([4, 1], '+', u'蓝色+-'), ([4], "+", "蓝色"),
([4, 1], '', u'蓝色-'), ([4], "", "蓝色"),
([4, 0], '+', u'蓝色+'), ([4, 1], "+", "蓝色+-"),
([4, 0], '', u'蓝色'), ([4, 1], "", "蓝色-"),
([12], '', u'蓝色--------'), ([4, 0], "+", "蓝色+"),
([6, 6], '', u'蓝色--------'), ([4, 0], "", "蓝色"),
([3, 3, 3, 3], '', u'蓝色--------'), ([12], "", "蓝色--------"),
([2, 1, 2, 1, 2, 1, 2, 1], '', u'蓝色--------'), ([6, 6], "", "蓝色--------"),
([1] * 12, '', u'蓝色--------'), ([3, 3, 3, 3], "", "蓝色--------"),
([2, 4], '', u'蓝色--'), ([2, 1, 2, 1, 2, 1, 2, 1], "", "蓝色--------"),
([1, 4], '', u'蓝色-'), ([1] * 12, "", "蓝色--------"),
([1, 3], '', u'蓝色'), ([2, 4], "", "蓝色--"),
([1, 2], '', u'---'), ([1, 4], "", "蓝色-"),
([2], '', u'--'), ([1, 3], "", "蓝色"),
([12], '+', u'蓝色--------'), ([1, 2], "", "---"),
([0, 12], '+', u'蓝色---------'), ([2], "", "--"),
([12, 0], '+', u'蓝色--------+'), ([12], "+", "蓝色--------"),
([0, 0, 12], '+', u'蓝色----------'), ([0, 12], "+", "蓝色---------"),
([12, 0, 0], '+', u'蓝色--------++'), ([12, 0], "+", "蓝色--------+"),
([3, 3], '+', u'蓝色---'), ([0, 0, 12], "+", "蓝色----------"),
([4, 2], '+', u'蓝色+--'), ([12, 0, 0], "+", "蓝色--------++"),
([5, 1], '+', u'蓝色-+-'), ([3, 3], "+", "蓝色---"),
([3, 3, 3, 3], '+', u'蓝色---+---+---'), ([4, 2], "+", "蓝色+--"),
([2, 2, 4, 4], '+', u'蓝色-+----+----'), ([5, 1], "+", "蓝色-+-"),
([1, 1, 5, 5], '+', u'蓝色-----+-----'), ([3, 3, 3, 3], "+", "蓝色---+---+---"),
([2, 2, 2, 2], '+', u'蓝色-+--+--'), ([2, 2, 4, 4], "+", "蓝色-+----+----"),
([1, 1, 1, 1, 1], '+', u'蓝色-+-+-'), ([1, 1, 5, 5], "+", "蓝色-----+-----"),
([0, 0, 0, 0, 0, 0, 0], '+', u'蓝色++'), ([2, 2, 2, 2], "+", "蓝色-+--+--"),
([1, 1], '+', u'-+-'), ([1, 1, 1, 1, 1], "+", "蓝色-+-+-"),
]) ([0, 0, 0, 0, 0, 0, 0], "+", "蓝色++"),
@pytest.mark.parametrize('left,right', [('', ''), ('<', '>')]) ([1, 1], "+", "-+-"),
],
)
@pytest.mark.parametrize("left,right", [("", ""), ("<", ">")])
def test_cjk(outer_widths, left, intersect, right, expected): def test_cjk(outer_widths, left, intersect, right, expected):
"""Test with CJK characters in title. """Test with CJK characters in title.
@ -179,48 +190,51 @@ def test_cjk(outer_widths, left, intersect, right, expected):
""" """
if left and right: if left and right:
expected = left + expected + right expected = left + expected + right
actual = build_border(outer_widths, '-', left, intersect, right, title=u'蓝色') actual = build_border(outer_widths, "-", left, intersect, right, title="蓝色")
assert ''.join(actual) == expected assert "".join(actual) == expected
@pytest.mark.parametrize('outer_widths,intersect,expected', [ @pytest.mark.parametrize(
([12], '+', u'معرب--------'), "outer_widths,intersect,expected",
([12], '', u'معرب--------'), [
([7, 5], '+', u'معرب---+-----'), ([12], "+", "معرب--------"),
([7, 5], '', u'معرب--------'), ([12], "", "معرب--------"),
([4], '+', u'معرب'), ([7, 5], "+", "معرب---+-----"),
([4], '', u'معرب'), ([7, 5], "", "معرب--------"),
([4, 1], '+', u'معرب+-'), ([4], "+", "معرب"),
([4, 1], '', u'معرب-'), ([4], "", "معرب"),
([4, 0], '+', u'معرب+'), ([4, 1], "+", "معرب+-"),
([4, 0], '', u'معرب'), ([4, 1], "", "معرب-"),
([12], '', u'معرب--------'), ([4, 0], "+", "معرب+"),
([6, 6], '', u'معرب--------'), ([4, 0], "", "معرب"),
([3, 3, 3, 3], '', u'معرب--------'), ([12], "", "معرب--------"),
([2, 1, 2, 1, 2, 1, 2, 1], '', u'معرب--------'), ([6, 6], "", "معرب--------"),
([1] * 12, '', u'معرب--------'), ([3, 3, 3, 3], "", "معرب--------"),
([2, 4], '', u'معرب--'), ([2, 1, 2, 1, 2, 1, 2, 1], "", "معرب--------"),
([1, 4], '', u'معرب-'), ([1] * 12, "", "معرب--------"),
([1, 3], '', u'معرب'), ([2, 4], "", "معرب--"),
([1, 2], '', u'---'), ([1, 4], "", "معرب-"),
([2], '', u'--'), ([1, 3], "", "معرب"),
([12], '+', u'معرب--------'), ([1, 2], "", "---"),
([0, 12], '+', u'معرب---------'), ([2], "", "--"),
([12, 0], '+', u'معرب--------+'), ([12], "+", "معرب--------"),
([0, 0, 12], '+', u'معرب----------'), ([0, 12], "+", "معرب---------"),
([12, 0, 0], '+', u'معرب--------++'), ([12, 0], "+", "معرب--------+"),
([3, 3], '+', u'معرب---'), ([0, 0, 12], "+", "معرب----------"),
([4, 2], '+', u'معرب+--'), ([12, 0, 0], "+", "معرب--------++"),
([5, 1], '+', u'معرب-+-'), ([3, 3], "+", "معرب---"),
([3, 3, 3, 3], '+', u'معرب---+---+---'), ([4, 2], "+", "معرب+--"),
([2, 2, 4, 4], '+', u'معرب-+----+----'), ([5, 1], "+", "معرب-+-"),
([1, 1, 5, 5], '+', u'معرب-----+-----'), ([3, 3, 3, 3], "+", "معرب---+---+---"),
([2, 2, 2, 2], '+', u'معرب-+--+--'), ([2, 2, 4, 4], "+", "معرب-+----+----"),
([1, 1, 1, 1, 1], '+', u'معرب-+-+-'), ([1, 1, 5, 5], "+", "معرب-----+-----"),
([0, 0, 0, 0, 0, 0, 0], '+', u'معرب++'), ([2, 2, 2, 2], "+", "معرب-+--+--"),
([1, 1], '+', u'-+-'), ([1, 1, 1, 1, 1], "+", "معرب-+-+-"),
]) ([0, 0, 0, 0, 0, 0, 0], "+", "معرب++"),
@pytest.mark.parametrize('left,right', [('', ''), ('<', '>')]) ([1, 1], "+", "-+-"),
],
)
@pytest.mark.parametrize("left,right", [("", ""), ("<", ">")])
def test_rtl(outer_widths, left, intersect, right, expected): def test_rtl(outer_widths, left, intersect, right, expected):
"""Test with RTL characters in title. """Test with RTL characters in title.
@ -232,53 +246,59 @@ def test_rtl(outer_widths, left, intersect, right, expected):
""" """
if left and right: if left and right:
expected = left + expected + right expected = left + expected + right
actual = build_border(outer_widths, '-', left, intersect, right, title=u'معرب') actual = build_border(outer_widths, "-", left, intersect, right, title="معرب")
assert ''.join(actual) == expected assert "".join(actual) == expected
@pytest.mark.parametrize('outer_widths,intersect,expected', [ @pytest.mark.parametrize(
([12], '+', '\x1b[34mTEST\x1b[0m--------'), "outer_widths,intersect,expected",
([12], '', '\x1b[34mTEST\x1b[0m--------'), [
([7, 5], '+', '\x1b[34mTEST\x1b[0m---+-----'), ([12], "+", "\x1b[34mTEST\x1b[0m--------"),
([7, 5], '', '\x1b[34mTEST\x1b[0m--------'), ([12], "", "\x1b[34mTEST\x1b[0m--------"),
([4], '+', '\x1b[34mTEST\x1b[0m'), ([7, 5], "+", "\x1b[34mTEST\x1b[0m---+-----"),
([4], '', '\x1b[34mTEST\x1b[0m'), ([7, 5], "", "\x1b[34mTEST\x1b[0m--------"),
([4, 1], '+', '\x1b[34mTEST\x1b[0m+-'), ([4], "+", "\x1b[34mTEST\x1b[0m"),
([4, 1], '', '\x1b[34mTEST\x1b[0m-'), ([4], "", "\x1b[34mTEST\x1b[0m"),
([4, 0], '+', '\x1b[34mTEST\x1b[0m+'), ([4, 1], "+", "\x1b[34mTEST\x1b[0m+-"),
([4, 0], '', '\x1b[34mTEST\x1b[0m'), ([4, 1], "", "\x1b[34mTEST\x1b[0m-"),
([12], '', '\x1b[34mTEST\x1b[0m--------'), ([4, 0], "+", "\x1b[34mTEST\x1b[0m+"),
([6, 6], '', '\x1b[34mTEST\x1b[0m--------'), ([4, 0], "", "\x1b[34mTEST\x1b[0m"),
([3, 3, 3, 3], '', '\x1b[34mTEST\x1b[0m--------'), ([12], "", "\x1b[34mTEST\x1b[0m--------"),
([2, 1, 2, 1, 2, 1, 2, 1], '', '\x1b[34mTEST\x1b[0m--------'), ([6, 6], "", "\x1b[34mTEST\x1b[0m--------"),
([1] * 12, '', '\x1b[34mTEST\x1b[0m--------'), ([3, 3, 3, 3], "", "\x1b[34mTEST\x1b[0m--------"),
([2, 4], '', '\x1b[34mTEST\x1b[0m--'), ([2, 1, 2, 1, 2, 1, 2, 1], "", "\x1b[34mTEST\x1b[0m--------"),
([1, 4], '', '\x1b[34mTEST\x1b[0m-'), ([1] * 12, "", "\x1b[34mTEST\x1b[0m--------"),
([1, 3], '', '\x1b[34mTEST\x1b[0m'), ([2, 4], "", "\x1b[34mTEST\x1b[0m--"),
([1, 2], '', '---'), ([1, 4], "", "\x1b[34mTEST\x1b[0m-"),
([12], '+', '\x1b[34mTEST\x1b[0m--------'), ([1, 3], "", "\x1b[34mTEST\x1b[0m"),
([0, 12], '+', '\x1b[34mTEST\x1b[0m---------'), ([1, 2], "", "---"),
([12, 0], '+', '\x1b[34mTEST\x1b[0m--------+'), ([12], "+", "\x1b[34mTEST\x1b[0m--------"),
([0, 0, 12], '+', '\x1b[34mTEST\x1b[0m----------'), ([0, 12], "+", "\x1b[34mTEST\x1b[0m---------"),
([12, 0, 0], '+', '\x1b[34mTEST\x1b[0m--------++'), ([12, 0], "+", "\x1b[34mTEST\x1b[0m--------+"),
([3, 3], '+', '\x1b[34mTEST\x1b[0m---'), ([0, 0, 12], "+", "\x1b[34mTEST\x1b[0m----------"),
([4, 2], '+', '\x1b[34mTEST\x1b[0m+--'), ([12, 0, 0], "+", "\x1b[34mTEST\x1b[0m--------++"),
([5, 1], '+', '\x1b[34mTEST\x1b[0m-+-'), ([3, 3], "+", "\x1b[34mTEST\x1b[0m---"),
([3, 3, 3, 3], '+', '\x1b[34mTEST\x1b[0m---+---+---'), ([4, 2], "+", "\x1b[34mTEST\x1b[0m+--"),
([2, 2, 4, 4], '+', '\x1b[34mTEST\x1b[0m-+----+----'), ([5, 1], "+", "\x1b[34mTEST\x1b[0m-+-"),
([1, 1, 5, 5], '+', '\x1b[34mTEST\x1b[0m-----+-----'), ([3, 3, 3, 3], "+", "\x1b[34mTEST\x1b[0m---+---+---"),
([2, 2, 2, 2], '+', '\x1b[34mTEST\x1b[0m-+--+--'), ([2, 2, 4, 4], "+", "\x1b[34mTEST\x1b[0m-+----+----"),
([1, 1, 1, 1, 1], '+', '\x1b[34mTEST\x1b[0m-+-+-'), ([1, 1, 5, 5], "+", "\x1b[34mTEST\x1b[0m-----+-----"),
([0, 0, 0, 0, 0, 0, 0], '+', '\x1b[34mTEST\x1b[0m++'), ([2, 2, 2, 2], "+", "\x1b[34mTEST\x1b[0m-+--+--"),
([1, 1], '+', '-+-'), ([1, 1, 1, 1, 1], "+", "\x1b[34mTEST\x1b[0m-+-+-"),
]) ([0, 0, 0, 0, 0, 0, 0], "+", "\x1b[34mTEST\x1b[0m++"),
@pytest.mark.parametrize('left,right', [('', ''), ('<', '>')]) ([1, 1], "+", "-+-"),
@pytest.mark.parametrize('title', [ ],
'\x1b[34mTEST\x1b[0m', )
Color('{blue}TEST{/all}'), @pytest.mark.parametrize("left,right", [("", ""), ("<", ">")])
Fore.BLUE + 'TEST' + Style.RESET_ALL, @pytest.mark.parametrize(
colored('TEST', 'blue'), "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): def test_colors(outer_widths, left, intersect, right, title, expected):
"""Test with color title characters. """Test with color title characters.
@ -291,16 +311,19 @@ def test_colors(outer_widths, left, intersect, right, title, expected):
""" """
if left and right: if left and right:
expected = left + expected + right expected = left + expected + right
actual = build_border(outer_widths, '-', left, intersect, right, title=title) actual = build_border(outer_widths, "-", left, intersect, right, title=title)
assert ''.join(actual) == expected assert "".join(actual) == expected
@pytest.mark.parametrize('outer_widths,title,expected', [ @pytest.mark.parametrize(
([3, 3, 3], 123, '<123+---+--->'), "outer_widths,title,expected",
([3, 3, 3], 0.9, '<0.9+---+--->'), [
([3, 3, 3], True, '<True---+--->'), ([3, 3, 3], 123, "<123+---+--->"),
([3, 3, 3], False, '<False--+--->'), ([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): def test_non_string(outer_widths, title, expected):
"""Test with non-string values. """Test with non-string values.
@ -308,5 +331,5 @@ def test_non_string(outer_widths, title, expected):
:param title: Title in border. :param title: Title in border.
:param str expected: Expected output. :param str expected: Expected output.
""" """
actual = build_border(outer_widths, '-', '<', '+', '>', title=title) actual = build_border(outer_widths, "-", "<", "+", ">", title=title)
assert ''.join(actual) == expected assert "".join(actual) == expected

View file

@ -1,16 +1,18 @@
"""Test function in module.""" """Test function in module."""
from terminaltables.build import build_row from terminaltables3.build import build_row
def test_one_line(): def test_one_line():
"""Test with one line cells.""" """Test with one line cells."""
row = [ 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 = [ expected = [
('>', 'Left Cell', '|', 'Center Cell', '|', 'Right Cell', '<'), (">", "Left Cell", "|", "Center Cell", "|", "Right Cell", "<"),
] ]
assert actual == expected assert actual == expected
@ -19,24 +21,22 @@ def test_two_line():
"""Test with two line cells.""" """Test with two line cells."""
row = [ row = [
[ [
'Left ', "Left ",
'Cell1', "Cell1",
], ],
[ [
'Center', "Center",
'Cell2 ', "Cell2 ",
], ],
[ [
'Right', "Right",
'Cell3', "Cell3",
], ],
] ]
actual = [tuple(i) for i in build_row(row, '>', '|', '<')] actual = [tuple(i) for i in build_row(row, ">", "|", "<")]
expected = [ expected = [
('>', 'Left ', '|', 'Center', '|', 'Right', '<'), (">", "Left ", "|", "Center", "|", "Right", "<"),
('>', 'Cell1', '|', 'Cell2 ', '|', 'Cell3', '<'), (">", "Cell1", "|", "Cell2 ", "|", "Cell3", "<"),
] ]
assert actual == expected assert actual == expected
@ -45,60 +45,58 @@ def test_three_line():
"""Test with three line cells.""" """Test with three line cells."""
row = [ row = [
[ [
'Left ', "Left ",
'Cell1', "Cell1",
' ', " ",
], ],
[ [
'Center', "Center",
'Cell2 ', "Cell2 ",
' ', " ",
], ],
[ [
'Right', "Right",
'Cell3', "Cell3",
' ', " ",
], ],
] ]
actual = [tuple(i) for i in build_row(row, '>', '|', '<')] actual = [tuple(i) for i in build_row(row, ">", "|", "<")]
expected = [ expected = [
('>', 'Left ', '|', 'Center', '|', 'Right', '<'), (">", "Left ", "|", "Center", "|", "Right", "<"),
('>', 'Cell1', '|', 'Cell2 ', '|', 'Cell3', '<'), (">", "Cell1", "|", "Cell2 ", "|", "Cell3", "<"),
('>', ' ', '|', ' ', '|', ' ', '<'), (">", " ", "|", " ", "|", " ", "<"),
] ]
assert actual == expected assert actual == expected
def test_single(): def test_single():
"""Test with single cell.""" """Test with single cell."""
actual = [tuple(i) for i in build_row([['Cell']], '>', '|', '<')] actual = [tuple(i) for i in build_row([["Cell"]], ">", "|", "<")]
expected = [ expected = [
('>', 'Cell', '<'), (">", "Cell", "<"),
] ]
assert actual == expected assert actual == expected
def test_empty(): def test_empty():
"""Test with empty cell.""" """Test with empty cell."""
actual = [tuple(i) for i in build_row([['']], '>', '|', '<')] actual = [tuple(i) for i in build_row([[""]], ">", "|", "<")]
expected = [ expected = [
('>', '', '<'), (">", "", "<"),
] ]
assert actual == expected assert actual == expected
def test_no_cells(): def test_no_cells():
"""Test with no cells.""" """Test with no cells."""
actual = [tuple(i) for i in build_row([[]], '>', '|', '<')] actual = [tuple(i) for i in build_row([[]], ">", "|", "<")]
expected = [ expected = [
('>', '<'), (">", "<"),
] ]
assert actual == expected assert actual == expected
actual = [tuple(i) for i in build_row([], '>', '|', '<')] actual = [tuple(i) for i in build_row([], ">", "|", "<")]
expected = [ expected = [
('>', '<'), (">", "<"),
] ]
assert actual == expected assert actual == expected

View file

@ -2,36 +2,36 @@
import pytest 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): def test_borders(generator):
"""Test with borders. """Test with borders.
:param bool generator: Test with generator instead of list. :param bool generator: Test with generator instead of list.
""" """
line = ['One', 'Two', 'Three'] line = ["One", "Two", "Three"]
actual = list(combine(iter(line) if generator else line, '>', '|', '<')) actual = list(combine(iter(line) if generator else line, ">", "|", "<"))
assert actual == ['>', 'One', '|', 'Two', '|', 'Three', '<'] assert actual == [">", "One", "|", "Two", "|", "Three", "<"]
@pytest.mark.parametrize('generator', [False, True]) @pytest.mark.parametrize("generator", [False, True])
def test_no_border(generator): def test_no_border(generator):
"""Test without borders. """Test without borders.
:param bool generator: Test with generator instead of list. :param bool generator: Test with generator instead of list.
""" """
line = ['One', 'Two', 'Three'] line = ["One", "Two", "Three"]
actual = list(combine(iter(line) if generator else line, '', '', '')) actual = list(combine(iter(line) if generator else line, "", "", ""))
assert actual == ['One', 'Two', 'Three'] assert actual == ["One", "Two", "Three"]
@pytest.mark.parametrize('generator', [False, True]) @pytest.mark.parametrize("generator", [False, True])
def test_no_items(generator): def test_no_items(generator):
"""Test with empty list. """Test with empty list.
:param bool generator: Test with generator instead of list. :param bool generator: Test with generator instead of list.
""" """
actual = list(combine(iter([]) if generator else [], '>', '|', '<')) actual = list(combine(iter([]) if generator else [], ">", "|", "<"))
assert actual == ['>', '<'] assert actual == [">", "<"]

View file

@ -1,25 +1,24 @@
"""Test function in module.""" """Test function in module."""
from terminaltables.build import flatten from terminaltables3.build import flatten
def test_one_line(): def test_one_line():
"""Test with one line cells.""" """Test with one line cells."""
table = [ table = [
['>', 'Left Cell', '|', 'Center Cell', '|', 'Right Cell', '<'], [">", "Left Cell", "|", "Center Cell", "|", "Right Cell", "<"],
] ]
actual = flatten(table) actual = flatten(table)
expected = '>Left Cell|Center Cell|Right Cell<' expected = ">Left Cell|Center Cell|Right Cell<"
assert actual == expected assert actual == expected
def test_two_line(): def test_two_line():
"""Test with two line cells.""" """Test with two line cells."""
table = [ table = [
['>', 'Left ', '|', 'Center', '|', 'Right', '<'], [">", "Left ", "|", "Center", "|", "Right", "<"],
['>', 'Cell1', '|', 'Cell2 ', '|', 'Cell3', '<'], [">", "Cell1", "|", "Cell2 ", "|", "Cell3", "<"],
] ]
actual = flatten(table) actual = flatten(table)
expected = ('>Left |Center|Right<\n' expected = ">Left |Center|Right<\n" ">Cell1|Cell2 |Cell3<"
'>Cell1|Cell2 |Cell3<')
assert actual == expected assert actual == expected

View file

@ -1,7 +1,5 @@
"""Test example scripts.""" """Test example scripts."""
from __future__ import print_function
import os import os
import subprocess import subprocess
import sys import sys
@ -11,17 +9,19 @@ import pytest
from tests import PROJECT_ROOT 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): def test(filename):
"""Test with subprocess. """Test with subprocess.
:param str filename: Example script filename to run. :param str filename: Example script filename to run.
""" """
command = [sys.executable, str(PROJECT_ROOT.join(filename))] command = [sys.executable, str(PROJECT_ROOT.join(filename))]
env = dict(os.environ, PYTHONIOENCODING='utf-8') env = dict(os.environ, PYTHONIOENCODING="utf-8")
# Run. # 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] output = proc.communicate()[0]
# Verify. # Verify.

View file

@ -1,17 +1,21 @@
"""Common objects used by tests in directory.""" """Common objects used by tests in directory."""
from terminaltables import terminal_io from terminaltables3 import terminal_io
class MockKernel32(object): class MockKernel32:
"""Mock kernel32.""" """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.""" """Constructor."""
self.stderr = stderr self.stderr = stderr
self.stdout = stdout 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_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_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.setConsoleTitleA_called = False
self.setConsoleTitleW_called = False self.setConsoleTitleW_called = False

View file

@ -1,11 +1,14 @@
# coding: utf-8
"""Test function in module.""" """Test function in module."""
import ctypes import ctypes
import pytest 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 from tests.test_terminal_io import MockKernel32

View file

@ -1,4 +1,3 @@
# coding: utf-8
"""Test function in module.""" """Test function in module."""
import sys import sys
@ -7,7 +6,7 @@ from textwrap import dedent
import py import py
import pytest 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 import PROJECT_ROOT
from tests.screenshot import RunNewConsole, screenshot_until_match 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() HERE = py.path.local(__file__).dirpath()
@pytest.mark.skip("Fails on windows, I didn't touch it") @pytest.mark.skip("Fails on windows, I didn't touch it")
@pytest.mark.parametrize('is_windows', [False, True]) @pytest.mark.parametrize("is_windows", [False, True])
@pytest.mark.parametrize('mode', ['ascii', 'unicode', 'bytes']) @pytest.mark.parametrize("mode", ["ascii", "unicode", "bytes"])
def test(monkeypatch, is_windows, mode): def test(monkeypatch, is_windows, mode):
"""Test function. """Test function.
@ -25,16 +25,16 @@ def test(monkeypatch, is_windows, mode):
:param bool is_windows: Monkeypatch terminal_io.IS_WINDOWS :param bool is_windows: Monkeypatch terminal_io.IS_WINDOWS
:param str mode: Scenario to test for. :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() kernel32 = MockKernel32()
# Title. # Title.
if mode == 'ascii': if mode == "ascii":
title = 'Testing terminaltables.' title = "Testing terminaltables3."
elif mode == 'unicode': elif mode == "unicode":
title = u'Testing terminaltables with unicode: 世界你好蓝色' title = "Testing terminaltables3 with unicode: 世界你好蓝色"
else: else:
title = b'Testing terminaltables with bytes.' title = b"Testing terminaltables3 with bytes."
# Run. # Run.
assert set_terminal_title(title, kernel32) assert set_terminal_title(title, kernel32)
@ -42,10 +42,10 @@ def test(monkeypatch, is_windows, mode):
return return
# Verify. # Verify.
if mode == 'ascii': if mode == "ascii":
assert kernel32.setConsoleTitleA_called assert kernel32.setConsoleTitleA_called
assert not kernel32.setConsoleTitleW_called assert not kernel32.setConsoleTitleW_called
elif mode == 'unicode': elif mode == "unicode":
assert not kernel32.setConsoleTitleA_called assert not kernel32.setConsoleTitleA_called
assert kernel32.setConsoleTitleW_called assert kernel32.setConsoleTitleW_called
else: else:
@ -54,35 +54,36 @@ def test(monkeypatch, is_windows, mode):
@pytest.mark.skipif(str(not IS_WINDOWS)) @pytest.mark.skipif(str(not IS_WINDOWS))
@pytest.mark.parametrize('mode', ['ascii', 'unicode', 'bytes']) @pytest.mark.parametrize("mode", ["ascii", "unicode", "bytes"])
@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, mode): def test_windows_screenshot(tmpdir, mode):
"""Test function on Windows in a new console window. Take a screenshot to verify it works. """Test function on Windows in a new console window. Take a screenshot to verify it works.
:param tmpdir: pytest fixture. :param tmpdir: pytest fixture.
:param str mode: Scenario to test for. :param str mode: Scenario to test for.
""" """
script = tmpdir.join('script.py') script = tmpdir.join("script.py")
command = [sys.executable, str(script)] command = [sys.executable, str(script)]
change_title = tmpdir.join('change_title') change_title = tmpdir.join("change_title")
screenshot = PROJECT_ROOT.join('test_terminal_io_{0}.png'.format(mode)) screenshot = PROJECT_ROOT.join(f"test_terminal_io_{mode}.png")
if screenshot.check(): if screenshot.check():
screenshot.remove() screenshot.remove()
# Determine title. # Determine title.
if mode == 'ascii': if mode == "ascii":
title = "'test ASCII test'" title = "'test ASCII test'"
elif mode == 'unicode': elif mode == "unicode":
title = u"u'test 世界你好蓝色 test'" title = "u'test 世界你好蓝色 test'"
else: else:
title = "b'test ASCII test'" title = "b'test ASCII test'"
# Generate script. # Generate script.
script_template = dedent(u"""\ script_template = dedent(
"""\
# coding: utf-8 # coding: utf-8
from __future__ import print_function from __future__ import print_function
import os, time import os, time
from terminaltables.terminal_io import set_terminal_title from terminaltables3.terminal_io import set_terminal_title
stop_after = time.time() + 20 stop_after = time.time() + 20
print('Waiting for FindWindowA() in RunNewConsole.__enter__()...') print('Waiting for FindWindowA() in RunNewConsole.__enter__()...')
@ -93,15 +94,18 @@ def test_windows_screenshot(tmpdir, mode):
print('Waiting for screenshot_until_match()...') print('Waiting for screenshot_until_match()...')
while not os.path.exists(r'{screenshot}') and time.time() < stop_after: while not os.path.exists(r'{screenshot}') and time.time() < stop_after:
time.sleep(0.5) 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. # Setup expected.
if mode == 'unicode': if mode == "unicode":
sub_images = [str(p) for p in HERE.listdir('sub_title_cjk_*.bmp')] sub_images = [str(p) for p in HERE.listdir("sub_title_cjk_*.bmp")]
else: 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 assert sub_images
# Run. # Run.

View file

@ -1,15 +1,20 @@
# coding: utf-8
"""Test function in module.""" """Test function in module."""
import pytest 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 from tests.test_terminal_io import MockKernel32
@pytest.mark.parametrize('stderr', [1, INVALID_HANDLE_VALUE]) @pytest.mark.parametrize("stderr", [1, INVALID_HANDLE_VALUE])
@pytest.mark.parametrize('stdout', [2, INVALID_HANDLE_VALUE]) @pytest.mark.parametrize("stdout", [2, INVALID_HANDLE_VALUE])
def test_windows(monkeypatch, stderr, stdout): def test_windows(monkeypatch, stderr, stdout):
"""Test function with IS_WINDOWS=True. """Test function with IS_WINDOWS=True.
@ -17,7 +22,7 @@ def test_windows(monkeypatch, stderr, stdout):
:param int stderr: Mock handle value. :param int stderr: Mock handle value.
:param int stdout: 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) kernel32 = MockKernel32(stderr=stderr, stdout=stdout)
width, height = terminal_size(kernel32) width, height = terminal_size(kernel32)
@ -48,7 +53,7 @@ def test_nix(monkeypatch):
assert height == DEFAULT_HEIGHT assert height == DEFAULT_HEIGHT
# Test mocked. # 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() width, height = terminal_size()
assert width == 119 assert width == 119
assert height == 29 assert height == 29

View file

@ -1,4 +1,3 @@
# coding: utf-8
"""Test function in module.""" """Test function in module."""
import pytest import pytest
@ -6,75 +5,74 @@ from colorama import Fore
from colorclass import Color from colorclass import Color
from termcolor import colored from termcolor import colored
from terminaltables.width_and_alignment import align_and_pad_cell from terminaltables3.width_and_alignment import align_and_pad_cell
@pytest.mark.parametrize('string,align,width,expected', [ @pytest.mark.parametrize(
('test', '', 4, ['test']), "string,align,width,expected",
(123, '', 3, ['123']), [
(0.9, '', 3, ['0.9']), ("test", "", 4, ["test"]),
(None, '', 4, ['None']), (123, "", 3, ["123"]),
(True, '', 4, ['True']), (0.9, "", 3, ["0.9"]),
(False, '', 5, ['False']), (None, "", 4, ["None"]),
(Color('{blue}Test{/blue}'), '', 4, ['\x1b[34mTest\x1b[39m']), (True, "", 4, ["True"]),
(Fore.BLUE + 'Test' + Fore.RESET, '', 4, ['\x1b[34mTest\x1b[39m']), (False, "", 5, ["False"]),
(colored('Test', 'blue'), '', 4, ['\x1b[34mTest\x1b[0m']), (Color("{blue}Test{/blue}"), "", 4, ["\x1b[34mTest\x1b[39m"]),
('蓝色', '', 4, ['蓝色']), (Fore.BLUE + "Test" + Fore.RESET, "", 4, ["\x1b[34mTest\x1b[39m"]),
(u'שלום', '', 4, [u'\u05e9\u05dc\u05d5\u05dd']), (colored("Test", "blue"), "", 4, ["\x1b[34mTest\x1b[0m"]),
(u'معرب', '', 4, [u'\u0645\u0639\u0631\u0628']), ("蓝色", "", 4, ["蓝色"]),
("שלום", "", 4, ["\u05e9\u05dc\u05d5\u05dd"]),
('test', '', 5, ['test ']), ("معرب", "", 4, ["\u0645\u0639\u0631\u0628"]),
(123, '', 4, ['123 ']), ("test", "", 5, ["test "]),
(0.9, '', 4, ['0.9 ']), (123, "", 4, ["123 "]),
(None, '', 5, ['None ']), (0.9, "", 4, ["0.9 "]),
(True, '', 5, ['True ']), (None, "", 5, ["None "]),
(False, '', 6, ['False ']), (True, "", 5, ["True "]),
(Color('{blue}Test{/blue}'), '', 5, ['\x1b[34mTest\x1b[39m ']), (False, "", 6, ["False "]),
(Fore.BLUE + 'Test' + Fore.RESET, '', 5, ['\x1b[34mTest\x1b[39m ']), (Color("{blue}Test{/blue}"), "", 5, ["\x1b[34mTest\x1b[39m "]),
(colored('Test', 'blue'), '', 5, ['\x1b[34mTest\x1b[0m ']), (Fore.BLUE + "Test" + Fore.RESET, "", 5, ["\x1b[34mTest\x1b[39m "]),
('蓝色', '', 5, ['蓝色 ']), (colored("Test", "blue"), "", 5, ["\x1b[34mTest\x1b[0m "]),
(u'שלום', '', 5, [u'\u05e9\u05dc\u05d5\u05dd ']), ("蓝色", "", 5, ["蓝色 "]),
(u'معرب', '', 5, [u'\u0645\u0639\u0631\u0628 ']), ("שלום", "", 5, ["\u05e9\u05dc\u05d5\u05dd "]),
("معرب", "", 5, ["\u0645\u0639\u0631\u0628 "]),
('test', 'left', 5, ['test ']), ("test", "left", 5, ["test "]),
(123, 'left', 4, ['123 ']), (123, "left", 4, ["123 "]),
(0.9, 'left', 4, ['0.9 ']), (0.9, "left", 4, ["0.9 "]),
(None, 'left', 5, ['None ']), (None, "left", 5, ["None "]),
(True, 'left', 5, ['True ']), (True, "left", 5, ["True "]),
(False, 'left', 6, ['False ']), (False, "left", 6, ["False "]),
(Color('{blue}Test{/blue}'), 'left', 5, ['\x1b[34mTest\x1b[39m ']), (Color("{blue}Test{/blue}"), "left", 5, ["\x1b[34mTest\x1b[39m "]),
(Fore.BLUE + 'Test' + Fore.RESET, '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 ']), (colored("Test", "blue"), "left", 5, ["\x1b[34mTest\x1b[0m "]),
('蓝色', 'left', 5, ['蓝色 ']), ("蓝色", "left", 5, ["蓝色 "]),
(u'שלום', 'left', 5, [u'\u05e9\u05dc\u05d5\u05dd ']), ("שלום", "left", 5, ["\u05e9\u05dc\u05d5\u05dd "]),
(u'معرب', 'left', 5, [u'\u0645\u0639\u0631\u0628 ']), ("معرب", "left", 5, ["\u0645\u0639\u0631\u0628 "]),
("test", "right", 5, [" test"]),
('test', 'right', 5, [' test']), (123, "right", 4, [" 123"]),
(123, 'right', 4, [' 123']), (0.9, "right", 4, [" 0.9"]),
(0.9, 'right', 4, [' 0.9']), (None, "right", 5, [" None"]),
(None, 'right', 5, [' None']), (True, "right", 5, [" True"]),
(True, 'right', 5, [' True']), (False, "right", 6, [" False"]),
(False, 'right', 6, [' False']), (Color("{blue}Test{/blue}"), "right", 5, [" \x1b[34mTest\x1b[39m"]),
(Color('{blue}Test{/blue}'), 'right', 5, [' \x1b[34mTest\x1b[39m']), (Fore.BLUE + "Test" + Fore.RESET, "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"]),
(colored('Test', 'blue'), 'right', 5, [' \x1b[34mTest\x1b[0m']), ("蓝色", "right", 5, [" 蓝色"]),
('蓝色', 'right', 5, [' 蓝色']), ("שלום", "right", 5, [" \u05e9\u05dc\u05d5\u05dd"]),
(u'שלום', 'right', 5, [u' \u05e9\u05dc\u05d5\u05dd']), ("معرب", "right", 5, [" \u0645\u0639\u0631\u0628"]),
(u'معرب', 'right', 5, [u' \u0645\u0639\u0631\u0628']), ("test", "center", 6, [" test "]),
(123, "center", 5, [" 123 "]),
('test', 'center', 6, [' test ']), (0.9, "center", 5, [" 0.9 "]),
(123, 'center', 5, [' 123 ']), (None, "center", 6, [" None "]),
(0.9, 'center', 5, [' 0.9 ']), (True, "center", 6, [" True "]),
(None, 'center', 6, [' None ']), (False, "center", 7, [" False "]),
(True, 'center', 6, [' True ']), (Color("{blue}Test{/blue}"), "center", 6, [" \x1b[34mTest\x1b[39m "]),
(False, 'center', 7, [' False ']), (Fore.BLUE + "Test" + Fore.RESET, "center", 6, [" \x1b[34mTest\x1b[39m "]),
(Color('{blue}Test{/blue}'), 'center', 6, [' \x1b[34mTest\x1b[39m ']), (colored("Test", "blue"), "center", 6, [" \x1b[34mTest\x1b[0m "]),
(Fore.BLUE + 'Test' + Fore.RESET, 'center', 6, [' \x1b[34mTest\x1b[39m ']), ("蓝色", "center", 6, [" 蓝色 "]),
(colored('Test', 'blue'), 'center', 6, [' \x1b[34mTest\x1b[0m ']), ("שלום", "center", 6, [" \u05e9\u05dc\u05d5\u05dd "]),
('蓝色', 'center', 6, [' 蓝色 ']), ("معرب", "center", 6, [" \u0645\u0639\u0631\u0628 "]),
(u'שלום', 'center', 6, [u' \u05e9\u05dc\u05d5\u05dd ']), ],
(u'معرب', 'center', 6, [u' \u0645\u0639\u0631\u0628 ']), )
])
def test_width(string, align, width, expected): def test_width(string, align, width, expected):
"""Test width and horizontal alignment. """Test width and horizontal alignment.
@ -87,47 +85,61 @@ def test_width(string, align, width, expected):
assert actual == expected assert actual == expected
@pytest.mark.parametrize('string,align,height,expected', [ @pytest.mark.parametrize(
('test', '', 1, ['test']), "string,align,height,expected",
(Color('{blue}Test{/blue}'), '', 1, ['\x1b[34mTest\x1b[39m']), [
(Fore.BLUE + 'Test' + Fore.RESET, '', 1, ['\x1b[34mTest\x1b[39m']), ("test", "", 1, ["test"]),
(colored('Test', 'blue'), '', 1, ['\x1b[34mTest\x1b[0m']), (Color("{blue}Test{/blue}"), "", 1, ["\x1b[34mTest\x1b[39m"]),
('蓝色', '', 1, ['蓝色']), (Fore.BLUE + "Test" + Fore.RESET, "", 1, ["\x1b[34mTest\x1b[39m"]),
(u'שלום', '', 1, [u'\u05e9\u05dc\u05d5\u05dd']), (colored("Test", "blue"), "", 1, ["\x1b[34mTest\x1b[0m"]),
(u'معرب', '', 1, [u'\u0645\u0639\u0631\u0628']), ("蓝色", "", 1, ["蓝色"]),
("שלום", "", 1, ["\u05e9\u05dc\u05d5\u05dd"]),
('test', '', 2, ['test', ' ']), ("معرب", "", 1, ["\u0645\u0639\u0631\u0628"]),
(Color('{blue}Test{/blue}'), '', 2, ['\x1b[34mTest\x1b[39m', ' ']), ("test", "", 2, ["test", " "]),
(Fore.BLUE + 'Test' + Fore.RESET, '', 2, ['\x1b[34mTest\x1b[39m', ' ']), (Color("{blue}Test{/blue}"), "", 2, ["\x1b[34mTest\x1b[39m", " "]),
(colored('Test', 'blue'), '', 2, ['\x1b[34mTest\x1b[0m', ' ']), (Fore.BLUE + "Test" + Fore.RESET, "", 2, ["\x1b[34mTest\x1b[39m", " "]),
('蓝色', '', 2, ['蓝色', ' ']), (colored("Test", "blue"), "", 2, ["\x1b[34mTest\x1b[0m", " "]),
(u'שלום', '', 2, [u'\u05e9\u05dc\u05d5\u05dd', ' ']), ("蓝色", "", 2, ["蓝色", " "]),
(u'معرب', '', 2, [u'\u0645\u0639\u0631\u0628', ' ']), ("שלום", "", 2, ["\u05e9\u05dc\u05d5\u05dd", " "]),
("معرب", "", 2, ["\u0645\u0639\u0631\u0628", " "]),
('test', 'top', 2, ['test', ' ']), ("test", "top", 2, ["test", " "]),
(Color('{blue}Test{/blue}'), 'top', 2, ['\x1b[34mTest\x1b[39m', ' ']), (Color("{blue}Test{/blue}"), "top", 2, ["\x1b[34mTest\x1b[39m", " "]),
(Fore.BLUE + 'Test' + Fore.RESET, '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', ' ']), (colored("Test", "blue"), "top", 2, ["\x1b[34mTest\x1b[0m", " "]),
('蓝色', 'top', 2, ['蓝色', ' ']), ("蓝色", "top", 2, ["蓝色", " "]),
(u'שלום', 'top', 2, [u'\u05e9\u05dc\u05d5\u05dd', ' ']), ("שלום", "top", 2, ["\u05e9\u05dc\u05d5\u05dd", " "]),
(u'معرب', 'top', 2, [u'\u0645\u0639\u0631\u0628', ' ']), ("معرب", "top", 2, ["\u0645\u0639\u0631\u0628", " "]),
("test", "bottom", 2, [" ", "test"]),
('test', 'bottom', 2, [' ', 'test']), (Color("{blue}Test{/blue}"), "bottom", 2, [" ", "\x1b[34mTest\x1b[39m"]),
(Color('{blue}Test{/blue}'), 'bottom', 2, [' ', '\x1b[34mTest\x1b[39m']), (
(Fore.BLUE + 'Test' + Fore.RESET, 'bottom', 2, [' ', '\x1b[34mTest\x1b[39m']), Fore.BLUE + "Test" + Fore.RESET,
(colored('Test', 'blue'), 'bottom', 2, [' ', '\x1b[34mTest\x1b[0m']), "bottom",
('蓝色', 'bottom', 2, [' ', '蓝色']), 2,
(u'שלום', 'bottom', 2, [' ', u'\u05e9\u05dc\u05d5\u05dd']), [" ", "\x1b[34mTest\x1b[39m"],
(u'معرب', 'bottom', 2, [' ', u'\u0645\u0639\u0631\u0628']), ),
(colored("Test", "blue"), "bottom", 2, [" ", "\x1b[34mTest\x1b[0m"]),
('test', 'middle', 3, [' ', 'test', ' ']), ("蓝色", "bottom", 2, [" ", "蓝色"]),
(Color('{blue}Test{/blue}'), 'middle', 3, [' ', '\x1b[34mTest\x1b[39m', ' ']), ("שלום", "bottom", 2, [" ", "\u05e9\u05dc\u05d5\u05dd"]),
(Fore.BLUE + 'Test' + Fore.RESET, 'middle', 3, [' ', '\x1b[34mTest\x1b[39m', ' ']), ("معرب", "bottom", 2, [" ", "\u0645\u0639\u0631\u0628"]),
(colored('Test', 'blue'), 'middle', 3, [' ', '\x1b[34mTest\x1b[0m', ' ']), ("test", "middle", 3, [" ", "test", " "]),
('蓝色', 'middle', 3, [' ', '蓝色', ' ']), (
(u'שלום', 'middle', 3, [' ', u'\u05e9\u05dc\u05d5\u05dd', ' ']), Color("{blue}Test{/blue}"),
(u'معرب', 'middle', 3, [' ', u'\u0645\u0639\u0631\u0628', ' ']), "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): def test_height(string, align, height, expected):
"""Test height and vertical alignment. """Test height and vertical alignment.
@ -140,57 +152,68 @@ def test_height(string, align, height, expected):
assert actual == expected assert actual == expected
@pytest.mark.parametrize('string,align,expected', [ @pytest.mark.parametrize(
('', '', ['.......', '.......', '.......', '.......', '.......']), "string,align,expected",
('\n', '', ['.......', '.......', '.......', '.......', '.......']), [
('a\nb\nc', '', ['.......', '.a.....', '.b.....', '.c.....', '.......']), ("", "", [".......", ".......", ".......", ".......", "......."]),
('test', '', ['.......', '.test..', '.......', '.......', '.......']), ("\n", "", [".......", ".......", ".......", ".......", "......."]),
("a\nb\nc", "", [".......", ".a.....", ".b.....", ".c.....", "......."]),
('', 'left', ['.......', '.......', '.......', '.......', '.......']), ("test", "", [".......", ".test..", ".......", ".......", "......."]),
('\n', 'left', ['.......', '.......', '.......', '.......', '.......']), ("", "left", [".......", ".......", ".......", ".......", "......."]),
('a\nb\nc', 'left', ['.......', '.a.....', '.b.....', '.c.....', '.......']), ("\n", "left", [".......", ".......", ".......", ".......", "......."]),
('test', 'left', ['.......', '.test..', '.......', '.......', '.......']), ("a\nb\nc", "left", [".......", ".a.....", ".b.....", ".c.....", "......."]),
("test", "left", [".......", ".test..", ".......", ".......", "......."]),
('', 'right', ['.......', '.......', '.......', '.......', '.......']), ("", "right", [".......", ".......", ".......", ".......", "......."]),
('\n', 'right', ['.......', '.......', '.......', '.......', '.......']), ("\n", "right", [".......", ".......", ".......", ".......", "......."]),
('a\nb\nc', 'right', ['.......', '.....a.', '.....b.', '.....c.', '.......']), ("a\nb\nc", "right", [".......", ".....a.", ".....b.", ".....c.", "......."]),
('test', 'right', ['.......', '..test.', '.......', '.......', '.......']), ("test", "right", [".......", "..test.", ".......", ".......", "......."]),
("", "center", [".......", ".......", ".......", ".......", "......."]),
('', 'center', ['.......', '.......', '.......', '.......', '.......']), ("\n", "center", [".......", ".......", ".......", ".......", "......."]),
('\n', 'center', ['.......', '.......', '.......', '.......', '.......']), ("a\nb\nc", "center", [".......", "...a...", "...b...", "...c...", "......."]),
('a\nb\nc', 'center', ['.......', '...a...', '...b...', '...c...', '.......']), ("test", "center", [".......", "..test.", ".......", ".......", "......."]),
('test', 'center', ['.......', '..test.', '.......', '.......', '.......']), ("", "top", [".......", ".......", ".......", ".......", "......."]),
("\n", "top", [".......", ".......", ".......", ".......", "......."]),
('', 'top', ['.......', '.......', '.......', '.......', '.......']), ("a\nb\nc", "top", [".......", ".a.....", ".b.....", ".c.....", "......."]),
('\n', 'top', ['.......', '.......', '.......', '.......', '.......']), ("test", "top", [".......", ".test..", ".......", ".......", "......."]),
('a\nb\nc', 'top', ['.......', '.a.....', '.b.....', '.c.....', '.......']), ("", "bottom", [".......", ".......", ".......", ".......", "......."]),
('test', 'top', ['.......', '.test..', '.......', '.......', '.......']), ("\n", "bottom", [".......", ".......", ".......", ".......", "......."]),
("a\nb\nc", "bottom", [".......", ".a.....", ".b.....", ".c.....", "......."]),
('', 'bottom', ['.......', '.......', '.......', '.......', '.......']), ("test", "bottom", [".......", ".......", ".......", ".test..", "......."]),
('\n', 'bottom', ['.......', '.......', '.......', '.......', '.......']), ("", "middle", [".......", ".......", ".......", ".......", "......."]),
('a\nb\nc', 'bottom', ['.......', '.a.....', '.b.....', '.c.....', '.......']), ("\n", "middle", [".......", ".......", ".......", ".......", "......."]),
('test', 'bottom', ['.......', '.......', '.......', '.test..', '.......']), ("a\nb\nc", "middle", [".......", ".a.....", ".b.....", ".c.....", "......."]),
("test", "middle", [".......", ".......", ".test..", ".......", "......."]),
('', 'middle', ['.......', '.......', '.......', '.......', '.......']), (
('\n', 'middle', ['.......', '.......', '.......', '.......', '.......']), "蓝色\nשלום\nمعرب",
('a\nb\nc', 'middle', ['.......', '.a.....', '.b.....', '.c.....', '.......']), "",
('test', 'middle', ['.......', '.......', '.test..', '.......', '.......']), [
".......",
( ".蓝色..",
u'蓝色\nשלום\nمعرب', ".\u05e9\u05dc\u05d5\u05dd..",
'', ".\u0645\u0639\u0631\u0628..",
['.......', 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'))), "\n".join(
'', (
['.......', '.\x1b[34mTest\x1b[39m..', '.\x1b[34mTest\x1b[39m..', '.\x1b[34mTest\x1b[0m..', '.......'] Color("{blue}Test{/blue}"),
), Fore.BLUE + "Test" + Fore.RESET,
colored("Test", "blue"),
# (Color('{blue}A\nB{/blue}'), '', '.......\n.\x1b[34mA\x1b[39m.....\n.\x1b[34mB\x1b[39m.....\n.......\n.......'), )
),
]) "",
[
".......",
".\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): def test_odd_width_height_pad_space(string, align, expected):
"""Test odd number width, height, padding, and dots for whitespaces. """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 str align: Alignment in any dimension but one at a time.
:param list expected: Expected output string. :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 assert actual == expected

View file

@ -2,7 +2,7 @@
import pytest 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) @pytest.fixture(autouse=True)
@ -11,27 +11,35 @@ def patch(monkeypatch):
:param monkeypatch: pytest fixture. :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(): def test_empty():
"""Test with zero-length cells.""" """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) == 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) == 69 assert (
column_max_width(max_dimensions([["", "", ""], ["", "", ""]])[0], 0, 2, 1, 2)
== 69
)
def test_single_line(): def test_single_line():
"""Test with single-line cells.""" """Test with single-line cells."""
table_data = [ table_data = [
['Name', 'Color', 'Type'], ["Name", "Color", "Type"],
['Avocado', 'green', 'nut'], ["Avocado", "green", "nut"],
['Tomato', 'red', 'fruit'], ["Tomato", "red", "fruit"],
['Lettuce', 'green', 'vegetable'], ["Lettuce", "green", "vegetable"],
] ]
inner_widths = max_dimensions(table_data)[0] 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 assert column_max_width(inner_widths, 2, outer, inner, padding) == 48
table_data = [ table_data = [
['Name', 'Color', 'Type'], ["Name", "Color", "Type"],
['Avocado', 'green', 'nut'], ["Avocado", "green", "nut"],
['Tomato', 'red', 'fruit'], ["Tomato", "red", "fruit"],
['Lettuce', 'green', 'vegetable'], ["Lettuce", "green", "vegetable"],
['Watermelon', 'green', 'fruit'], ["Watermelon", "green", "fruit"],
] ]
inner_widths = max_dimensions(table_data)[0] inner_widths = max_dimensions(table_data)[0]
outer, inner, padding = 2, 1, 2 outer, inner, padding = 2, 1, 2
@ -91,10 +99,15 @@ def test_multi_line(monkeypatch):
:param monkeypatch: pytest fixture. :param monkeypatch: pytest fixture.
""" """
table_data = [ table_data = [
['Show', 'Characters'], ["Show", "Characters"],
['Rugrats', ('Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\n' [
'Susie Carmichael, Dil Pickles, Kimi Finster, Spike')], "Rugrats",
['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] (
"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] inner_widths = max_dimensions(table_data)[0]
outer, inner, padding = 2, 1, 2 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, 0, outer, inner, padding) == -11
assert column_max_width(inner_widths, 1, outer, inner, padding) == 62 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, 0, outer, inner, padding) == 10
assert column_max_width(inner_widths, 1, outer, inner, padding) == 83 assert column_max_width(inner_widths, 1, outer, inner, padding) == 83

View file

@ -1,4 +1,3 @@
# coding: utf-8
"""Test function in module.""" """Test function in module."""
import pytest import pytest
@ -6,18 +5,20 @@ from colorama import Fore
from colorclass import Color from colorclass import Color
from termcolor import colored 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', [ @pytest.mark.parametrize(
([[]], [], [0]), "table_data,expected_w,expected_h",
([['']], [0], [0]), [
([['', '']], [0, 0], [0]), ([[]], [], [0]),
([[""]], [0], [0]),
([[], []], [], [0, 0]), ([["", ""]], [0, 0], [0]),
([[''], ['']], [0], [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): def test_zero_length(table_data, expected_w, expected_h):
"""Test zero-length or empty tables. """Test zero-length or empty tables.
@ -32,40 +33,60 @@ def test_zero_length(table_data, expected_w, expected_h):
def test_single_line(): def test_single_line():
"""Test widths.""" """Test widths."""
table_data = [ table_data = [
['Name', 'Color', 'Type'], ["Name", "Color", "Type"],
['Avocado', 'green', 'nut'], ["Avocado", "green", "nut"],
['Tomato', 'red', 'fruit'], ["Tomato", "red", "fruit"],
['Lettuce', 'green', 'vegetable'], ["Lettuce", "green", "vegetable"],
] ]
assert max_dimensions(table_data, 1, 1) == ([7, 5, 9], [1, 1, 1, 1], [9, 7, 11], [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']) 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, 2, 2) == (
[10, 5, 9],
[1, 1, 1, 1, 1],
[14, 9, 13],
[1, 1, 1, 1, 1],
)
def test_multi_line(): def test_multi_line():
"""Test heights.""" """Test heights."""
table_data = [ 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]) assert max_dimensions(table_data, 0, 0, 1, 1) == ([3, 6], [3], [3, 6], [5])
table_data = [ table_data = [
['Show', 'Characters'], ["Show", "Characters"],
['Rugrats', ('Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\n' [
'Susie Carmichael, Dil Pickles, Kimi Finster, Spike')], "Rugrats",
['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] (
"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(): def test_trailing_newline():
r"""Test with trailing \n.""" r"""Test with trailing \n."""
table_data = [ table_data = [
['Row One\n<blank>'], ["Row One\n<blank>"],
['<blank>\nRow Two'], ["<blank>\nRow Two"],
['Row Three\n'], ["Row Three\n"],
['\nRow Four'], ["\nRow Four"],
] ]
assert max_dimensions(table_data) == ([9], [2, 2, 2, 2], [9], [2, 2, 2, 2]) 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(): def test_colors_cjk_rtl():
"""Test color text, CJK characters, and RTL characters.""" """Test color text, CJK characters, and RTL characters."""
table_data = [ table_data = [
[Color('{blue}Test{/blue}')], [Color("{blue}Test{/blue}")],
[Fore.BLUE + 'Test' + Fore.RESET], [Fore.BLUE + "Test" + Fore.RESET],
[colored('Test', 'blue')], [colored("Test", "blue")],
] ]
assert max_dimensions(table_data) == ([4], [1, 1, 1], [4], [1, 1, 1]) assert max_dimensions(table_data) == ([4], [1, 1, 1], [4], [1, 1, 1])
table_data = [ table_data = [
['蓝色'], ["蓝色"],
['世界你好'], ["世界你好"],
] ]
assert max_dimensions(table_data) == ([8], [1, 1], [8], [1, 1]) assert max_dimensions(table_data) == ([8], [1, 1], [8], [1, 1])
table_data = [ table_data = [
['שלום'], ["שלום"],
['معرب'], ["معرب"],
] ]
assert max_dimensions(table_data) == ([4], [1, 1], [4], [1, 1]) assert max_dimensions(table_data) == ([4], [1, 1], [4], [1, 1])

View file

@ -1,26 +1,28 @@
"""Test function in module.""" """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(): def test_empty():
"""Test with zero-length cells.""" """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) == 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) == 10 assert (
table_width(max_dimensions([["", "", ""], ["", "", ""]], 1, 1)[2], 2, 1) == 10
)
def test_single_line(): def test_single_line():
"""Test with single-line cells.""" """Test with single-line cells."""
table_data = [ table_data = [
['Name', 'Color', 'Type'], ["Name", "Color", "Type"],
['Avocado', 'green', 'nut'], ["Avocado", "green", "nut"],
['Tomato', 'red', 'fruit'], ["Tomato", "red", "fruit"],
['Lettuce', 'green', 'vegetable'], ["Lettuce", "green", "vegetable"],
] ]
# '| Lettuce | green | vegetable |' # '| Lettuce | green | vegetable |'
@ -48,11 +50,11 @@ def test_single_line():
assert table_width(outer_widths, outer, inner) == 40 assert table_width(outer_widths, outer, inner) == 40
table_data = [ table_data = [
['Name', 'Color', 'Type'], ["Name", "Color", "Type"],
['Avocado', 'green', 'nut'], ["Avocado", "green", "nut"],
['Tomato', 'red', 'fruit'], ["Tomato", "red", "fruit"],
['Lettuce', 'green', 'vegetable'], ["Lettuce", "green", "vegetable"],
['Watermelon', 'green', 'fruit'], ["Watermelon", "green", "fruit"],
] ]
outer, inner, outer_widths = 2, 1, max_dimensions(table_data, 1, 1)[2] outer, inner, outer_widths = 2, 1, max_dimensions(table_data, 1, 1)[2]
assert table_width(outer_widths, outer, inner) == 34 assert table_width(outer_widths, outer, inner) == 34
@ -61,10 +63,15 @@ def test_single_line():
def test_multi_line(): def test_multi_line():
"""Test with multi-line cells.""" """Test with multi-line cells."""
table_data = [ table_data = [
['Show', 'Characters'], ["Show", "Characters"],
['Rugrats', ('Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\n' [
'Susie Carmichael, Dil Pickles, Kimi Finster, Spike')], "Rugrats",
['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] (
"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] outer, inner, outer_widths = 2, 1, max_dimensions(table_data, 1, 1)[2]
assert table_width(outer_widths, outer, inner) == 100 assert table_width(outer_widths, outer, inner) == 100

View file

@ -1,4 +1,3 @@
# coding: utf-8
"""Test function in module.""" """Test function in module."""
import pytest import pytest
@ -6,50 +5,49 @@ from colorama import Fore
from colorclass import Color from colorclass import Color
from termcolor import colored from termcolor import colored
from terminaltables.width_and_alignment import visible_width from terminaltables3.width_and_alignment import visible_width
@pytest.mark.parametrize('string,expected', [ @pytest.mark.parametrize(
# str "string,expected",
('hello, world', 12), [
('世界你好', 8), # str
('蓝色', 4), ("hello, world", 12),
('שלום', 4), ("世界你好", 8),
('معرب', 4), ("蓝色", 4),
('hello 世界', 10), ("שלום", 4),
("معرب", 4),
# str+ansi ("hello 世界", 10),
('\x1b[34mhello, world\x1b[39m', 12), # str+ansi
('\x1b[34m世界你好\x1b[39m', 8), ("\x1b[34mhello, world\x1b[39m", 12),
('\x1b[34m蓝色\x1b[39m', 4), ("\x1b[34m世界你好\x1b[39m", 8),
('\x1b[34mשלום\x1b[39m', 4), ("\x1b[34m蓝色\x1b[39m", 4),
('\x1b[34mمعرب\x1b[39m', 4), ("\x1b[34mשלום\x1b[39m", 4),
('\x1b[34mhello 世界\x1b[39m', 10), ("\x1b[34mمعرب\x1b[39m", 4),
("\x1b[34mhello 世界\x1b[39m", 10),
# colorclass # colorclass
(Color(u'{blue}hello, world{/blue}'), 12), (Color("{blue}hello, world{/blue}"), 12),
(Color(u'{blue}世界你好{/blue}'), 8), (Color("{blue}世界你好{/blue}"), 8),
(Color(u'{blue}蓝色{/blue}'), 4), (Color("{blue}蓝色{/blue}"), 4),
(Color(u'{blue}שלום{/blue}'), 4), (Color("{blue}שלום{/blue}"), 4),
(Color(u'{blue}معرب{/blue}'), 4), (Color("{blue}معرب{/blue}"), 4),
(Color(u'{blue}hello 世界{/blue}'), 10), (Color("{blue}hello 世界{/blue}"), 10),
# colorama
# colorama (Fore.BLUE + "hello, world" + Fore.RESET, 12),
(Fore.BLUE + 'hello, world' + Fore.RESET, 12), (Fore.BLUE + "世界你好" + Fore.RESET, 8),
(Fore.BLUE + '世界你好' + Fore.RESET, 8), (Fore.BLUE + "蓝色" + Fore.RESET, 4),
(Fore.BLUE + '蓝色' + Fore.RESET, 4), (Fore.BLUE + "שלום" + Fore.RESET, 4),
(Fore.BLUE + 'שלום' + Fore.RESET, 4), (Fore.BLUE + "معرب" + Fore.RESET, 4),
(Fore.BLUE + 'معرب' + Fore.RESET, 4), (Fore.BLUE + "hello 世界" + Fore.RESET, 10),
(Fore.BLUE + 'hello 世界' + Fore.RESET, 10), # termcolor
(colored("hello, world", "blue"), 12),
# termcolor (colored("世界你好", "blue"), 8),
(colored('hello, world', 'blue'), 12), (colored("蓝色", "blue"), 4),
(colored('世界你好', 'blue'), 8), (colored("שלום", "blue"), 4),
(colored('蓝色', 'blue'), 4), (colored("معرب", "blue"), 4),
(colored('שלום', 'blue'), 4), (colored("hello 世界", "blue"), 10),
(colored('معرب', 'blue'), 4), ],
(colored('hello 世界', 'blue'), 10), )
])
def test(string, expected): def test(string, expected):
"""Test function with different color libraries. """Test function with different color libraries.

65
tox.ini
View file

@ -2,39 +2,23 @@
name = terminaltables name = terminaltables
[tox] [tox]
envlist = lint,py{34,27,26} envlist = py{38,39,310,311,312,313}
[testenv] [testenv]
commands = commands =
python -c "import os, sys; sys.platform == 'win32' and os.system('easy_install pillow')" 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} --cov-config tox.ini {posargs:tests} py.test --cov-report term-missing --cov-report xml --cov {[general]name} {posargs:tests}
deps = deps =
colorama==0.3.7 colorama>=0.3.7
colorclass==2.2.0 colorclass>=2.2.0
pytest-cov==2.4.0 pytest-cov>=2.4.0
termcolor==1.1.0 termcolor>=1.1.0
passenv = passenv =
WINDIR WINDIR
setenv = setenv =
PYTHON_EGG_CACHE = {envtmpdir} PYTHON_EGG_CACHE = {envtmpdir}
usedevelop = True 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] [testenv:docs]
changedir = {toxinidir}/docs changedir = {toxinidir}/docs
commands = commands =
@ -55,25 +39,24 @@ passenv =
HOME HOME
HOSTNAME HOSTNAME
SSH_AUTH_SOCK SSH_AUTH_SOCK
TRAVIS*
USER USER
usedevelop = False usedevelop = False
;
;[flake8]
;exclude = .tox/*,build/*,docs/*,env/*,get-pip.py
;import-order-style = smarkets
;max-line-length = 120
;statistics = True
[flake8] ;[pylint]
exclude = .tox/*,build/*,docs/*,env/*,get-pip.py ;disable =
import-order-style = smarkets ; locally-disabled,
max-line-length = 120 ; too-few-public-methods,
statistics = True ; too-many-instance-attributes,
;ignore = .tox/*,build/*,docs/*,env/*,get-pip.py
;max-args = 6
;max-line-length = 120
;reports = no
[pylint] ;[run]
disable = ;branch = True
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