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 LC_ALL=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/
requirements*.txt
.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/>`_.
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

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
Easily draw tables in terminal/console applications from a list of lists of strings. Supports multi-line rows.
- Python 2.6, 2.7, PyPy, PyPy3, 3.3, 3.4, and 3.5+ supported on Linux and OS X.
- Python 2.7, 3.3, 3.4, and 3.5+ supported on Windows (both 32 and 64 bit versions of Python).
Tested on Python 3.8+
**This is a fork of the terminaltables project. Which is archived and unmaintained. This library is in a new namespace
but should otherwise be a drop in replacement. Maintaining goals consist of maintaining ecosystem compatibility, type
annotations and responding to community pull requests.**
To Upgrade
==========
Replace all instances of `terminaltables` with `terminaltables3` in your code. If other libraries depend on `terminaltables`
in your venv they will not conflict because it is a new namespace.
As of right now, the documentation as the robpol86 version.
📖 Full documentation: https://robpol86.github.io/terminaltables
@ -15,19 +23,19 @@ Quickstart
Install:
```bash
pip install terminaltables
pip install terminaltables3
```
Usage:
```python
from terminaltables import AsciiTable
from terminaltables3 import AsciiTable
table_data = [
['Heading1', 'Heading2'],
['row1 column1', 'row1 column2'],
['row2 column1', 'row2 column2'],
['row3 column1', 'row3 column2']
["Heading1", "Heading2"],
["row1 column1", "row1 column2"],
["row2 column1", "row2 column2"],
["row3 column1", "row3 column2"],
]
table = AsciiTable(table_data)
print
@ -54,4 +62,4 @@ Source code for examples:
- [example2.py](https://github.com/matthewdeanmartin/terminaltables/blob/master/example2.py)
- [example3.py](https://github.com/matthewdeanmartin/terminaltables/blob/master/example3.py)
[Change Log](https://github.com/matthewdeanmartin/terminaltables/blob/master/CHANGELOG.md)
[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.
sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), '..')))
author = '@Robpol86'
copyright = '{}, {}'.format(time.strftime('%Y'), author)
html_last_updated_fmt = '%c {}'.format(time.tzname[time.localtime().tm_isdst])
master_doc = 'index'
project = __import__('setup').NAME
pygments_style = 'friendly'
release = version = __import__('setup').VERSION
templates_path = ['_templates']
extensions = list()
sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), "..")))
author = "@Robpol86"
copyright = "{}, {}".format(time.strftime("%Y"), author)
html_last_updated_fmt = f"%c {time.tzname[time.localtime().tm_isdst]}"
master_doc = "index"
project = __import__("setup").NAME
pygments_style = "friendly"
release = version = __import__("setup").VERSION
templates_path = ["_templates"]
extensions = []
# Options for HTML output.
html_context = dict(
conf_py_path='/docs/',
conf_py_path="/docs/",
display_github=True,
github_repo=os.environ.get('TRAVIS_REPO_SLUG', '/' + project).split('/', 1)[1],
github_user=os.environ.get('TRAVIS_REPO_SLUG', 'robpol86/').split('/', 1)[0],
github_version=os.environ.get('TRAVIS_BRANCH', 'master'),
source_suffix='.rst',
github_repo=os.environ.get("TRAVIS_REPO_SLUG", "/" + project).split("/", 1)[1],
github_user=os.environ.get("TRAVIS_REPO_SLUG", "robpol86/").split("/", 1)[0],
github_version=os.environ.get("TRAVIS_BRANCH", "master"),
source_suffix=".rst",
)
html_copy_source = False
html_favicon = 'favicon.ico'
html_theme = 'sphinx_rtd_theme'
html_favicon = "favicon.ico"
html_theme = "sphinx_rtd_theme"
html_title = project
# autodoc
extensions.append('sphinx.ext.autodoc')
extensions.append("sphinx.ext.autodoc")
# extlinks.
extensions.append('sphinx.ext.extlinks')
extlinks = {'github': ('https://github.com/robpol86/{0}/blob/v{1}/%s'.format(project, version), '')}
extensions.append("sphinx.ext.extlinks")
extlinks = {
"github": (
f"https://github.com/robpol86/{project}/blob/v{version}/%s",
"",
)
}
# google analytics
extensions.append('sphinxcontrib.googleanalytics')
googleanalytics_id = 'UA-82627369-1'
extensions.append("sphinxcontrib.googleanalytics")
googleanalytics_id = "UA-82627369-1"
# SCVersioning.
scv_banner_greatest_tag = True
scv_grm_exclude = ('.gitignore', '.nojekyll', 'README.rst')
scv_overflow = ('-W',)
scv_grm_exclude = (".gitignore", ".nojekyll", "README.rst")
scv_overflow = ("-W",)
scv_show_banner = True
scv_sort = ('semver', 'time')
scv_sort = ("semver", "time")

View file

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

View file

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

View file

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

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]
name = "terminaltables"
version = "3.1.10"
description = "Generate simple tables in terminals from a nested list of strings."
name = "terminaltables3"
version = "4.0.0"
description = "Generate simple tables in terminals from a nested list of strings. Fork of terminaltables."
authors = [
"Robpol86 <robpol86@gmail.com>",
"Matthew Martin <matthewdeanmartin@gmail.com>"
@ -18,49 +18,74 @@ classifiers = [
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX :: Linux",
"Operating System :: POSIX",
"Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Software Development :: Libraries",
"Topic :: Terminals",
"Topic :: Text Processing :: Markup",
]
packages = [
{ include = "terminaltables" },
{ include = "terminaltables3" },
]
include = [
"terminaltables/**/*.py",
"terminaltables3/**/*.py",
]
exclude = [
]
license = "MIT"
readme = "README.md"
repository = "https://github.com/matthewdeanmartin/terminaltables"
homepage = "https://github.com/matthewdeanmartin/terminaltables"
documentation = "https://github.com/matthewdeanmartin/terminaltables"
repository = "https://github.com/matthewdeanmartin/terminaltables3"
homepage = "https://github.com/matthewdeanmartin/terminaltables3"
documentation = "https://github.com/matthewdeanmartin/terminaltables3"
[tool.poetry.urls]
"Bug Tracker" = "https://github.com/matthewdeanmartin/terminaltables/issues"
"Change Log" = "https://github.com/matthewdeanmartin/terminaltables/blob/master/CHANGELOG.md"
"Bug Tracker" = "https://github.com/matthewdeanmartin/terminaltables3/issues"
"Change Log" = "https://github.com/matthewdeanmartin/terminaltables3/blob/master/CHANGELOG.md"
[tool.poetry.scripts]
[tool.poetry.dependencies]
# per vermin's estimation
python = ">=2.6 || >=3.0"
python = ">=3.8"
[tool.poetry.dev-dependencies]
pytest = "==6.0.1"
# complementary deps
colorama=">=0.3.7"
colorclass=">=2.2.0"
termcolor = "*"
# build deps
pytest = ">=8.2.1"
pytest-cov = ">=5.0.0"
vermin = ">=1.6.0"
tox = ">=4.15.0"
isort = ">=5.13.2"
bandit = ">=1.7.8"
black = ">=24.4.2"
pylint = ">=3.2.2"
ruff = ">=0.4.5"
mypy = ">=1.10.0"
pillow = ">=10.3.0"
pre-commit = ">=2"
# pytest = ">=7.4.3"
pytest-xdist =">=3.5.0"
pytest-randomly=">=3.15.0"
pytest-sugar =">=0.9.7"
# pytest-snapshot = "*"
# pytest-asyncio = "*"
pytest-network= "*"
# check-wheel-contents = "*" # requires <4
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

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."""
from terminaltables.base_table import BaseTable
from terminaltables.terminal_io import terminal_size
from terminaltables.width_and_alignment import column_max_width, max_dimensions, table_width
from terminaltables3.base_table import BaseTable
from terminaltables3.terminal_io import terminal_size
from terminaltables3.width_and_alignment import (
column_max_width,
max_dimensions,
table_width,
)
class AsciiTable(BaseTable):
@ -20,7 +24,7 @@ class AsciiTable(BaseTable):
:ivar int padding_right: Number of spaces to pad on the right side of every cell.
"""
def column_max_width(self, column_number):
def column_max_width(self, column_number: int) -> int:
"""Return the maximum width of a column based on the current terminal width.
:param int column_number: The column number to query.
@ -32,24 +36,28 @@ class AsciiTable(BaseTable):
outer_border = 2 if self.outer_border else 0
inner_border = 1 if self.inner_column_border else 0
padding = self.padding_left + self.padding_right
return column_max_width(inner_widths, column_number, outer_border, inner_border, padding)
return column_max_width(
inner_widths, column_number, outer_border, inner_border, padding
)
@property
def column_widths(self):
def column_widths(self) -> list[int]:
"""Return a list of integers representing the widths of each table column without padding."""
if not self.table_data:
return list()
return []
return max_dimensions(self.table_data)[0]
@property
def ok(self): # Too late to change API. # pylint: disable=invalid-name
def ok(self) -> bool: # Too late to change API. # pylint: disable=invalid-name
"""Return True if the table fits within the terminal width, False if the table breaks."""
return self.table_width <= terminal_size()[0]
@property
def table_width(self):
def table_width(self) -> int:
"""Return the width of the table including padding and borders."""
outer_widths = max_dimensions(self.table_data, self.padding_left, self.padding_right)[2]
outer_widths = max_dimensions(
self.table_data, self.padding_left, self.padding_right
)[2]
outer_border = 2 if self.outer_border else 0
inner_border = 1 if self.inner_column_border else 0
return table_width(outer_widths, outer_border, inner_border)

View file

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

View file

@ -1,9 +1,18 @@
"""Combine cells into rows."""
from terminaltables.width_and_alignment import visible_width
from typing import Generator, Iterator, Optional, Sequence, Union
from terminaltables3.width_and_alignment import visible_width
def combine(line, left, intersect, right):
def combine(
line: Union[
Generator[Union[int, str], None, None], Iterator[Optional[Union[int, str]]]
],
left: str,
intersect: Optional[str],
right: str,
) -> Generator[int, None, None]:
"""Zip borders between items in `line`.
e.g. ('l', '1', 'c', '2', 'c', '3', 'r')
@ -41,15 +50,21 @@ def combine(line, left, intersect, right):
yield intersect
item = peek
else:
for i in line:
yield i
yield from line
# Yield right border.
# Yield right border.
if right:
yield right
def build_border(outer_widths, horizontal, left, intersect, right, title=None):
def build_border(
outer_widths: Sequence[int],
horizontal: str,
left: str,
intersect: str,
right: str,
title: Optional[str] = None,
):
"""Build the top/bottom/middle row. Optionally embed the table title within the border.
Title is hidden if it doesn't fit between the left/right characters/edges.
@ -86,9 +101,13 @@ def build_border(outer_widths, horizontal, left, intersect, right, title=None):
# Handle title fitting in the first column.
if length == outer_widths[0]:
return combine([title] + [horizontal * c for c in outer_widths[1:]], left, intersect, right)
return combine(
[title] + [horizontal * c for c in outer_widths[1:]], left, intersect, right
)
if length < outer_widths[0]:
columns = [title + horizontal * (outer_widths[0] - length)] + [horizontal * c for c in outer_widths[1:]]
columns = [title + horizontal * (outer_widths[0] - length)] + [
horizontal * c for c in outer_widths[1:]
]
return combine(columns, left, intersect, right)
# Handle wide titles/narrow columns.
@ -96,7 +115,9 @@ def build_border(outer_widths, horizontal, left, intersect, right, title=None):
for width in combine(outer_widths, None, bool(intersect), None):
# If title is taken care of.
if length < 1:
columns_and_intersects.append(intersect if width is True else horizontal * width)
columns_and_intersects.append(
intersect if width is True else horizontal * width
)
# If title's last character overrides an intersect character.
elif width is True and length == 1:
length = 0
@ -105,7 +126,9 @@ def build_border(outer_widths, horizontal, left, intersect, right, title=None):
length -= 1
# If title's last character is within a column.
elif width >= length:
columns_and_intersects[0] += horizontal * (width - length) # Append horizontal chars to title.
columns_and_intersects[0] += horizontal * (
width - length
) # Append horizontal chars to title.
length = 0
# If remainder of title won't fit in a column.
else:
@ -148,4 +171,4 @@ def flatten(table):
:return: Joined rows/cells.
:rtype: str
"""
return '\n'.join(''.join(r) for r in table)
return "\n".join("".join(r) for r in table)

View file

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

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

View file

@ -2,36 +2,37 @@
import re
import unicodedata
from typing import Sequence, Tuple
from terminaltables.terminal_io import terminal_size
from terminaltables3.terminal_io import terminal_size
RE_COLOR_ANSI = re.compile(r'(\033\[[\d;]+m)')
RE_COLOR_ANSI = re.compile(r"(\033\[[\d;]+m)")
def visible_width(string):
def visible_width(string: str) -> int:
"""Get the visible width of a unicode string.
Some CJK unicode characters are more than one byte unlike ASCII and latin unicode characters.
From: https://github.com/Robpol86/terminaltables/pull/9
From: https://github.com/Robpol86/terminaltables3/pull/9
:param str string: String to measure.
:return: String's width.
:rtype: int
"""
if '\033' in string:
string = RE_COLOR_ANSI.sub('', string)
if "\033" in string:
string = RE_COLOR_ANSI.sub("", string)
# Convert to unicode.
try:
string = string.decode('u8')
string = string.decode("u8")
except (AttributeError, UnicodeEncodeError):
pass
width = 0
for char in string:
if unicodedata.east_asian_width(char) in ('F', 'W'):
if unicodedata.east_asian_width(char) in ("F", "W"):
width += 2
else:
width += 1
@ -39,7 +40,13 @@ def visible_width(string):
return width
def align_and_pad_cell(string, align, inner_dimensions, padding, space=' '):
def align_and_pad_cell(
string: str,
align: Tuple,
inner_dimensions: Tuple,
padding: Sequence[int],
space: str = " ",
) -> list[str]:
"""Align a string horizontally and vertically. Also add additional padding in both dimensions.
:param str string: Input string to operate on.
@ -51,37 +58,55 @@ def align_and_pad_cell(string, align, inner_dimensions, padding, space=' '):
:return: Padded cell split into lines.
:rtype: list
"""
if not hasattr(string, 'splitlines'):
if not hasattr(string, "splitlines"):
string = str(string)
# Handle trailing newlines or empty strings, str.splitlines() does not satisfy.
lines = string.splitlines() or ['']
if string.endswith('\n'):
lines.append('')
lines = string.splitlines() or [""]
if string.endswith("\n"):
lines.append("")
# Vertically align and pad.
if 'bottom' in align:
lines = ([''] * (inner_dimensions[1] - len(lines) + padding[2])) + lines + ([''] * padding[3])
elif 'middle' in align:
if "bottom" in align:
lines = (
([""] * (inner_dimensions[1] - len(lines) + padding[2]))
+ lines
+ ([""] * padding[3])
)
elif "middle" in align:
delta = inner_dimensions[1] - len(lines)
lines = ([''] * (delta // 2 + delta % 2 + padding[2])) + lines + ([''] * (delta // 2 + padding[3]))
lines = (
([""] * (delta // 2 + delta % 2 + padding[2]))
+ lines
+ ([""] * (delta // 2 + padding[3]))
)
else:
lines = ([''] * padding[2]) + lines + ([''] * (inner_dimensions[1] - len(lines) + padding[3]))
lines = (
([""] * padding[2])
+ lines
+ ([""] * (inner_dimensions[1] - len(lines) + padding[3]))
)
# Horizontally align and pad.
for i, line in enumerate(lines):
new_width = inner_dimensions[0] + len(line) - visible_width(line)
if 'right' in align:
if "right" in align:
lines[i] = line.rjust(padding[0] + new_width, space) + (space * padding[1])
elif 'center' in align:
lines[i] = (space * padding[0]) + line.center(new_width, space) + (space * padding[1])
elif "center" in align:
lines[i] = (
(space * padding[0])
+ line.center(new_width, space)
+ (space * padding[1])
)
else:
lines[i] = (space * padding[0]) + line.ljust(new_width + padding[1], space)
return lines
def max_dimensions(table_data, padding_left=0, padding_right=0, padding_top=0, padding_bottom=0):
def max_dimensions(
table_data, padding_left=0, padding_right=0, padding_top=0, padding_bottom=0
):
"""Get maximum widths of each column and maximum height of each row.
:param iter table_data: List of list of strings (unmodified table data).
@ -99,12 +124,15 @@ def max_dimensions(table_data, padding_left=0, padding_right=0, padding_top=0, p
# Find max width and heights.
for j, row in enumerate(table_data):
for i, cell in enumerate(row):
if not hasattr(cell, 'count') or not hasattr(cell, 'splitlines'):
if not hasattr(cell, "count") or not hasattr(cell, "splitlines"):
cell = str(cell)
if not cell:
continue
inner_heights[j] = max(inner_heights[j], cell.count('\n') + 1)
inner_widths[i] = max(inner_widths[i], *[visible_width(l) for l in cell.splitlines()])
inner_heights[j] = max(inner_heights[j], cell.count("\n") + 1)
inner_widths[i] = max(
inner_widths[i],
*[visible_width(the_line) for the_line in cell.splitlines()]
)
# Calculate with padding.
outer_widths = [padding_left + i + padding_right for i in inner_widths]
@ -113,7 +141,13 @@ def max_dimensions(table_data, padding_left=0, padding_right=0, padding_top=0, p
return inner_widths, inner_heights, outer_widths, outer_heights
def column_max_width(inner_widths, column_number, outer_border, inner_border, padding):
def column_max_width(
inner_widths: Sequence[int],
column_number: int,
outer_border: int,
inner_border: int,
padding: int,
) -> int:
"""Determine the maximum width of a column based on the current terminal width.
:param iter inner_widths: List of widths (no padding) for each column.
@ -138,7 +172,9 @@ def column_max_width(inner_widths, column_number, outer_border, inner_border, pa
return terminal_width - data_space - non_data_space
def table_width(outer_widths, outer_border, inner_border):
def table_width(
outer_widths: Sequence[int], outer_border: int, inner_border: int
) -> int:
"""Determine the width of the entire table including borders and padding.
:param iter outer_widths: List of widths (with padding) for each column.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,19 +2,22 @@
import pytest
from terminaltables.other_tables import AsciiTable
from terminaltables3.other_tables import AsciiTable
SINGLE_LINE = (
('Name', 'Color', 'Type'),
('Avocado', 'green', 'nut'),
('Tomato', 'red', 'fruit'),
('Lettuce', 'green', 'vegetable'),
("Name", "Color", "Type"),
("Avocado", "green", "nut"),
("Tomato", "red", "fruit"),
("Lettuce", "green", "vegetable"),
)
MULTI_LINE = (
('Show', 'Characters'),
('Rugrats', 'Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles'),
('South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'),
("Show", "Characters"),
(
"Rugrats",
"Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles",
),
("South Park", "Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick"),
)
@ -24,20 +27,25 @@ def patch(monkeypatch):
:param monkeypatch: pytest fixture.
"""
monkeypatch.setattr('terminaltables.ascii_table.terminal_size', lambda: (79, 24))
monkeypatch.setattr('terminaltables.width_and_alignment.terminal_size', lambda: (79, 24))
monkeypatch.setattr("terminaltables3.ascii_table.terminal_size", lambda: (79, 24))
monkeypatch.setattr(
"terminaltables3.width_and_alignment.terminal_size", lambda: (79, 24)
)
@pytest.mark.parametrize('table_data,column_number,expected', [
([], 0, IndexError),
([[]], 0, IndexError),
([['']], 1, IndexError),
(SINGLE_LINE, 0, 55),
(SINGLE_LINE, 1, 53),
(SINGLE_LINE, 2, 57),
(MULTI_LINE, 0, -11),
(MULTI_LINE, 1, 62),
])
@pytest.mark.parametrize(
"table_data,column_number,expected",
[
([], 0, IndexError),
([[]], 0, IndexError),
([[""]], 1, IndexError),
(SINGLE_LINE, 0, 55),
(SINGLE_LINE, 1, 53),
(SINGLE_LINE, 2, 57),
(MULTI_LINE, 0, -11),
(MULTI_LINE, 1, 62),
],
)
def test_column_max_width(table_data, column_number, expected):
"""Test method in class.
@ -47,7 +55,7 @@ def test_column_max_width(table_data, column_number, expected):
"""
table = AsciiTable(table_data)
if expected == IndexError:
if expected is IndexError:
with pytest.raises(IndexError):
table.column_max_width(column_number)
return
@ -58,22 +66,25 @@ def test_column_max_width(table_data, column_number, expected):
def test_column_widths():
"""Test method in class."""
assert AsciiTable([]).column_widths == list()
assert AsciiTable([]).column_widths == []
table = AsciiTable(SINGLE_LINE)
actual = table.column_widths
assert actual == [7, 5, 9]
@pytest.mark.parametrize('table_data,terminal_width,expected', [
([], None, True),
([[]], None, True),
([['']], None, True),
(SINGLE_LINE, None, True),
(SINGLE_LINE, 30, False),
(MULTI_LINE, None, False),
(MULTI_LINE, 100, True),
])
@pytest.mark.parametrize(
"table_data,terminal_width,expected",
[
([], None, True),
([[]], None, True),
([[""]], None, True),
(SINGLE_LINE, None, True),
(SINGLE_LINE, 30, False),
(MULTI_LINE, None, False),
(MULTI_LINE, 100, True),
],
)
def test_ok(monkeypatch, table_data, terminal_width, expected):
"""Test method in class.
@ -83,20 +94,25 @@ def test_ok(monkeypatch, table_data, terminal_width, expected):
:param bool expected: Expected return value.
"""
if terminal_width is not None:
monkeypatch.setattr('terminaltables.ascii_table.terminal_size', lambda: (terminal_width, 24))
monkeypatch.setattr(
"terminaltables3.ascii_table.terminal_size", lambda: (terminal_width, 24)
)
table = AsciiTable(table_data)
actual = table.ok
assert actual is expected
@pytest.mark.parametrize('table_data,expected', [
([], 2),
([[]], 2),
([['']], 4),
([[' ']], 5),
(SINGLE_LINE, 31),
(MULTI_LINE, 100),
])
@pytest.mark.parametrize(
"table_data,expected",
[
([], 2),
([[]], 2),
([[""]], 4),
([[" "]], 5),
(SINGLE_LINE, 31),
(MULTI_LINE, 100),
],
)
def test_table_width(table_data, expected):
"""Test method in class.

View file

View file

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

View file

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

View file

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

View file

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

View file

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,5 @@
"""Test example scripts."""
from __future__ import print_function
import os
import subprocess
import sys
@ -11,17 +9,19 @@ import pytest
from tests import PROJECT_ROOT
@pytest.mark.parametrize('filename', map('example{0}.py'.format, (1, 2, 3)))
@pytest.mark.parametrize("filename", map("example{}.py".format, (1, 2, 3)))
def test(filename):
"""Test with subprocess.
:param str filename: Example script filename to run.
"""
command = [sys.executable, str(PROJECT_ROOT.join(filename))]
env = dict(os.environ, PYTHONIOENCODING='utf-8')
env = dict(os.environ, PYTHONIOENCODING="utf-8")
# Run.
proc = subprocess.Popen(command, env=env, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
proc = subprocess.Popen(
command, env=env, stderr=subprocess.STDOUT, stdout=subprocess.PIPE
)
output = proc.communicate()[0]
# Verify.

View file

@ -1,17 +1,21 @@
"""Common objects used by tests in directory."""
from terminaltables import terminal_io
from terminaltables3 import terminal_io
class MockKernel32(object):
class MockKernel32:
"""Mock kernel32."""
def __init__(self, stderr=terminal_io.INVALID_HANDLE_VALUE, stdout=terminal_io.INVALID_HANDLE_VALUE):
def __init__(
self,
stderr=terminal_io.INVALID_HANDLE_VALUE,
stdout=terminal_io.INVALID_HANDLE_VALUE,
):
"""Constructor."""
self.stderr = stderr
self.stdout = stdout
self.csbi_err = b'x\x00)#\x00\x00\x87\x05\x07\x00\x00\x00j\x05w\x00\x87\x05x\x00J\x00' # 119 x 29
self.csbi_out = b'L\x00,\x01\x00\x00*\x01\x07\x00\x00\x00\x0e\x01K\x00*\x01L\x00L\x00' # 75 x 28
self.csbi_err = b"x\x00)#\x00\x00\x87\x05\x07\x00\x00\x00j\x05w\x00\x87\x05x\x00J\x00" # 119 x 29
self.csbi_out = b"L\x00,\x01\x00\x00*\x01\x07\x00\x00\x00\x0e\x01K\x00*\x01L\x00L\x00" # 75 x 28
self.setConsoleTitleA_called = False
self.setConsoleTitleW_called = False

View file

@ -1,11 +1,14 @@
# coding: utf-8
"""Test function in module."""
import ctypes
import pytest
from terminaltables.terminal_io import get_console_info, INVALID_HANDLE_VALUE, IS_WINDOWS
from terminaltables3.terminal_io import (
get_console_info,
INVALID_HANDLE_VALUE,
IS_WINDOWS,
)
from tests.test_terminal_io import MockKernel32

View file

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

View file

@ -1,15 +1,20 @@
# coding: utf-8
"""Test function in module."""
import pytest
from terminaltables.terminal_io import DEFAULT_HEIGHT, DEFAULT_WIDTH, INVALID_HANDLE_VALUE, IS_WINDOWS, terminal_size
from terminaltables3.terminal_io import (
DEFAULT_HEIGHT,
DEFAULT_WIDTH,
INVALID_HANDLE_VALUE,
IS_WINDOWS,
terminal_size,
)
from tests.test_terminal_io import MockKernel32
@pytest.mark.parametrize('stderr', [1, INVALID_HANDLE_VALUE])
@pytest.mark.parametrize('stdout', [2, INVALID_HANDLE_VALUE])
@pytest.mark.parametrize("stderr", [1, INVALID_HANDLE_VALUE])
@pytest.mark.parametrize("stdout", [2, INVALID_HANDLE_VALUE])
def test_windows(monkeypatch, stderr, stdout):
"""Test function with IS_WINDOWS=True.
@ -17,7 +22,7 @@ def test_windows(monkeypatch, stderr, stdout):
:param int stderr: Mock handle value.
:param int stdout: Mock handle value.
"""
monkeypatch.setattr('terminaltables.terminal_io.IS_WINDOWS', True)
monkeypatch.setattr("terminaltables3.terminal_io.IS_WINDOWS", True)
kernel32 = MockKernel32(stderr=stderr, stdout=stdout)
width, height = terminal_size(kernel32)
@ -48,7 +53,7 @@ def test_nix(monkeypatch):
assert height == DEFAULT_HEIGHT
# Test mocked.
monkeypatch.setattr('fcntl.ioctl', lambda *_: b'\x1d\x00w\x00\xca\x02\x96\x01')
monkeypatch.setattr("fcntl.ioctl", lambda *_: b"\x1d\x00w\x00\xca\x02\x96\x01")
width, height = terminal_size()
assert width == 119
assert height == 29

View file

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

View file

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

View file

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

View file

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

View file

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

65
tox.ini
View file

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