From 2c092ef71eb0ea0cdd19fe4fcd0d3398f512e3d9 Mon Sep 17 00:00:00 2001 From: John McNamara Date: Mon, 18 Nov 2024 20:53:49 +0000 Subject: [PATCH] sqa: add pylint fixes --- .github/workflows/python-app.yml | 2 +- .pylintrc | 645 +++++++++++++++++ Makefile | 2 + dev/docs/source/working_with_data.rst | 20 +- dev/docs/source/working_with_polars.rst | 5 +- dev/docs/source/worksheet.rst | 4 +- examples/.pylintrc | 650 ++++++++++++++++++ examples/autofilter.py | 2 +- examples/django_simple.py | 2 + examples/dynamic_arrays.py | 4 +- examples/inheritance1.py | 4 +- examples/inheritance2.py | 10 +- examples/pandas_chart_line.py | 2 +- examples/pandas_datetime.py | 2 +- examples/polars_chart.py | 2 +- examples/polars_multiple.py | 2 +- examples/polars_positioning.py | 4 +- examples/polars_sparklines.py | 3 +- examples/polars_xlsxwriter.py | 2 +- examples/rich_strings.py | 4 +- examples/unicode_polish_utf8.py | 36 +- examples/unicode_shift_jis.py | 36 +- examples/user_types1.py | 6 +- examples/user_types2.py | 6 +- examples/user_types3.py | 6 +- examples/vba_extract.py | 11 +- setup.py | 2 +- xlsxwriter/app.py | 2 +- xlsxwriter/chart.py | 98 ++- xlsxwriter/chart_area.py | 4 +- xlsxwriter/chart_bar.py | 7 +- xlsxwriter/chart_column.py | 4 +- xlsxwriter/chart_doughnut.py | 8 +- xlsxwriter/chart_line.py | 4 +- xlsxwriter/chart_pie.py | 9 +- xlsxwriter/chart_radar.py | 4 +- xlsxwriter/chart_scatter.py | 7 +- xlsxwriter/chart_stock.py | 6 +- xlsxwriter/chartsheet.py | 4 +- xlsxwriter/comments.py | 11 +- xlsxwriter/contenttypes.py | 40 +- xlsxwriter/core.py | 2 +- xlsxwriter/custom.py | 2 +- xlsxwriter/drawing.py | 45 +- xlsxwriter/format.py | 294 ++++++-- xlsxwriter/metadata.py | 12 +- xlsxwriter/packager.py | 9 +- xlsxwriter/relationships.py | 14 +- xlsxwriter/rich_value.py | 2 +- xlsxwriter/rich_value_rel.py | 2 +- xlsxwriter/rich_value_structure.py | 6 +- xlsxwriter/rich_value_types.py | 14 - xlsxwriter/shape.py | 35 +- xlsxwriter/sharedstrings.py | 18 +- xlsxwriter/styles.py | 7 +- xlsxwriter/table.py | 2 +- .../test/comparison/test_cond_format01.py | 2 +- .../test/comparison/test_cond_format08.py | 2 +- .../test/comparison/test_cond_format09.py | 2 +- xlsxwriter/test/comparison/test_format13.py | 2 +- xlsxwriter/test/comparison/test_format20.py | 2 +- xlsxwriter/test/comparison/test_format21.py | 2 +- xlsxwriter/test/comparison/test_format22.py | 2 +- xlsxwriter/test/comparison/test_format23.py | 2 +- .../test/comparison/test_hyperlink10.py | 8 +- .../test/comparison/test_hyperlink11.py | 2 +- .../test/comparison/test_hyperlink12.py | 12 +- .../test/comparison/test_hyperlink13.py | 6 +- .../test/comparison/test_hyperlink14.py | 6 +- .../test/comparison/test_hyperlink20.py | 4 +- .../test/comparison/test_hyperlink28.py | 8 +- .../test/comparison/test_hyperlink29.py | 4 +- .../test/comparison/test_hyperlink30.py | 8 +- .../test/comparison/test_merge_cells01.py | 10 +- .../test/comparison/test_merge_range01.py | 4 +- .../test/comparison/test_merge_range02.py | 4 +- .../test/comparison/test_merge_range03.py | 8 +- .../test/comparison/test_merge_range04.py | 4 +- .../test/comparison/test_merge_range05.py | 4 +- xlsxwriter/test/comparison/test_optimize03.py | 2 +- .../test/comparison/test_rich_string06.py | 2 +- .../test/comparison/test_rich_string08.py | 4 +- xlsxwriter/test/comparison/test_types10.py | 4 +- xlsxwriter/test/styles/test_write_font.py | 8 +- xlsxwriter/theme.py | 6 +- xlsxwriter/utility.py | 101 ++- xlsxwriter/vml.py | 14 - xlsxwriter/workbook.py | 76 +- xlsxwriter/worksheet.py | 316 ++++----- xlsxwriter/xmlwriter.py | 5 +- 90 files changed, 2125 insertions(+), 667 deletions(-) create mode 100644 .pylintrc create mode 100644 examples/.pylintrc diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index a6cc0146f..ed88373bd 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -27,7 +27,7 @@ jobs: - name: Install test dependencies run: | python -m pip install --upgrade pip - pip install pytest flake8 black ruff + pip install pytest flake8 black ruff pylint - name: Test the code style run: | diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 000000000..b54b5916e --- /dev/null +++ b/.pylintrc @@ -0,0 +1,645 @@ +[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 + +# Clear in-memory caches upon conclusion of linting. Useful if running pylint +# in a server-like mode. +clear-cache-post-run=no + +# Load and enable all available extensions. Use --list-extensions to see a list +# all available extensions. +#enable-all-extensions= + +# In error mode, messages with a category besides ERROR or FATAL are +# suppressed, and no reports are done by default. Error mode is compatible with +# disabling specific errors. +#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 under which the program will exit 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 regular expressions patterns to the +# ignore-list. The regex matches against paths and can be in Posix or Windows +# format. Because '\\' represents the directory delimiter on Windows systems, +# it can't be used as an escape character. +ignore-paths= + +# Files or directories matching the regular expression 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 and +# will not be imported (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, and will cap the count on Windows to +# avoid hangs. +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 + +# Resolve imports to .pyi stubs if available. May reduce no-member messages and +# increase not-an-iterable messages. +prefer-stubs=no + +# Minimum Python version to use for version dependent checks. Will default to +# the version used to run pylint. +py-version=3.11 + +# Discover python modules and packages in the file system subtree. +recursive=no + +# Add paths to the list of the source roots. Supports globbing patterns. The +# source root is an absolute path or a path relative to the current working +# directory used to determine a package namespace for modules located under the +# source root. +source-roots= + +# 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= + + +[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 alias names. If left empty, type +# alias names will be checked with the set naming style. +#typealias-rgx= + +# 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, + asyncSetUp, + __post_init__ + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make,os._exit + +# 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=mcs + + +[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*(# )??$ + +# 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=100 + +# 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 explicit reexports by alias from a package __init__. +allow-reexport-from-package=no + +# 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 + + +[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=missing-module-docstring, + protected-access, + similarities, + too-few-public-methods, + too-many-arguments, + too-many-branches, + too-many-instance-attributes, + too-many-lines, + too-many-locals, + too-many-public-methods, + too-many-statements, + +# 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= + + +[METHOD_ARGS] + +# List of qualified names (i.e., library.method) which require a timeout +# parameter e.g. 'requests.api.get,requests.api.post' +timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request + + +[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 + +# Let 'consider-using-join' be raised when the separator to join on would be +# non-empty (resulting in expected fixes of the type: ``"- " + " - +# ".join(items)``) +suggest-join-with-non-empty-separator=yes + + +[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, +# json2 (improved json format), json (old json format) 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 + + +[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. No available dictionaries : You need to install +# both the python package and the system dependency for enchant to work. +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. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io diff --git a/Makefile b/Makefile index f40a24f26..b6074d4f4 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,8 @@ lint: @ruff check xlsxwriter/test --ignore=E501,F841 @ruff check examples @black --check xlsxwriter/ examples/ + @pylint xlsxwriter/*.py + @pylint --rcfile=examples/.pylintrc examples/*.py tags: $(Q)rm -f TAGS diff --git a/dev/docs/source/working_with_data.rst b/dev/docs/source/working_with_data.rst index 0c846adb3..f18fe6899 100644 --- a/dev/docs/source/working_with_data.rst +++ b/dev/docs/source/working_with_data.rst @@ -250,8 +250,8 @@ As an example, say you wanted to modify ``write()`` to automatically write takes the uuid, converts it to a string and then writes it using :func:`write_string`:: - def write_uuid(worksheet, row, col, uuid, format=None): - return worksheet.write_string(row, col, str(uuid), format) + def write_uuid(worksheet, row, col, uuid, cell_format=None): + return worksheet.write_string(row, col, str(uuid), cell_format) You could then add a handler that matches the ``uuid`` type and calls your user defined function:: @@ -324,8 +324,8 @@ The syntax of write handler functions Functions used in the :func:`add_write_handler` method should have the following method signature/parameters:: - def my_function(worksheet, row, col, token, format=None): - return worksheet.write_string(row, col, token, format) + def my_function(worksheet, row, col, token, cell_format=None): + return worksheet.write_string(row, col, token, cell_format) The function will be passed a :ref:`worksheet ` instance, an integer ``row`` and ``col`` value, a ``token`` that matches the type added to @@ -347,11 +347,11 @@ passwords with '\*\*\*\*' when writing string data. If your data was structured so that the password data was in the second column, apart from the header row, you could write a handler function like this:: - def hide_password(worksheet, row, col, string, format=None): + def hide_password(worksheet, row, col, string, cell_format=None): if col == 1 and row > 0: - return worksheet.write_string(row, col, '****', format) + return worksheet.write_string(row, col, '****', cell_format) else: - return worksheet.write_string(row, col, string, format) + return worksheet.write_string(row, col, string, cell_format) .. image:: _images/user_types5.png @@ -377,9 +377,9 @@ doesn't support them. You could create a handler function like the following that matched against floats and which wrote a blank cell if it was a ``NaN`` or else just returned to ``write()`` to continue as normal:: - def ignore_nan(worksheet, row, col, number, format=None): + def ignore_nan(worksheet, row, col, number, cell_format=None): if math.isnan(number): - return worksheet.write_blank(row, col, None, format) + return worksheet.write_blank(row, col, None, cell_format) else: # Return control to the calling write() method. return None @@ -387,7 +387,7 @@ or else just returned to ``write()`` to continue as normal:: If you wanted to just drop the ``NaN`` values completely and not add any formatting to the cell you could just return 0, for no error:: - def ignore_nan(worksheet, row, col, number, format=None): + def ignore_nan(worksheet, row, col, number, cell_format=None): if math.isnan(number): return 0 else: diff --git a/dev/docs/source/working_with_polars.rst b/dev/docs/source/working_with_polars.rst index 67ec44548..87f4482ab 100644 --- a/dev/docs/source/working_with_polars.rst +++ b/dev/docs/source/working_with_polars.rst @@ -199,7 +199,7 @@ worksheet:: workbook=workbook, worksheet="Sheet1", position="C8", - has_header=False) + include_header=False) Output: @@ -292,7 +292,6 @@ Adding Sparklines to the output dataframe We can also add :ref:`sparklines ` to the dataframe output:: import polars as pl - from polars.datatypes import INTEGER_DTYPES df = pl.DataFrame( { @@ -312,7 +311,7 @@ We can also add :ref:`sparklines ` to the dataframe output:: table_style="Table Style Light 2", # Specify an Excel number format for integer types. - dtype_formats={INTEGER_DTYPES: "#,##0_);(#,##0)"}, + dtype_formats={pl.Int32: "#,##0_);(#,##0)"}, # Configure sparklines to the dataframe. sparklines={ diff --git a/dev/docs/source/worksheet.rst b/dev/docs/source/worksheet.rst index 9bc73c8c9..8808b4bf5 100644 --- a/dev/docs/source/worksheet.rst +++ b/dev/docs/source/worksheet.rst @@ -164,9 +164,9 @@ For example, say you wanted to automatically write :mod:`uuid` values as strings using ``write()`` you would start by creating a function that takes the uuid, converts it to a string and then writes it using :func:`write_string`:: - def write_uuid(worksheet, row, col, uuid, format=None): + def write_uuid(worksheet, row, col, uuid, cell_format=None): string_uuid = str(uuid) - return worksheet.write_string(row, col, string_uuid, format) + return worksheet.write_string(row, col, string_uuid, cell_format) You could then add a handler that matches the ``uuid`` type and calls your user defined function:: diff --git a/examples/.pylintrc b/examples/.pylintrc new file mode 100644 index 000000000..5cdc82e06 --- /dev/null +++ b/examples/.pylintrc @@ -0,0 +1,650 @@ +[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 + +# Clear in-memory caches upon conclusion of linting. Useful if running pylint +# in a server-like mode. +clear-cache-post-run=no + +# Load and enable all available extensions. Use --list-extensions to see a list +# all available extensions. +#enable-all-extensions= + +# In error mode, messages with a category besides ERROR or FATAL are +# suppressed, and no reports are done by default. Error mode is compatible with +# disabling specific errors. +#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 under which the program will exit 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 regular expressions patterns to the +# ignore-list. The regex matches against paths and can be in Posix or Windows +# format. Because '\\' represents the directory delimiter on Windows systems, +# it can't be used as an escape character. +ignore-paths= + +# Files or directories matching the regular expression 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 and +# will not be imported (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='import sys; sys.path.append(".")' + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use, and will cap the count on Windows to +# avoid hangs. +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 + +# Resolve imports to .pyi stubs if available. May reduce no-member messages and +# increase not-an-iterable messages. +prefer-stubs=no + +# Minimum Python version to use for version dependent checks. Will default to +# the version used to run pylint. +py-version=3.11 + +# Discover python modules and packages in the file system subtree. +recursive=no + +# Add paths to the list of the source roots. Supports globbing patterns. The +# source root is an absolute path or a path relative to the current working +# directory used to determine a package namespace for modules located under the +# source root. +source-roots= + +# 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= + + +[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 alias names. If left empty, type +# alias names will be checked with the set naming style. +#typealias-rgx= + +# 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, + asyncSetUp, + __post_init__ + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make,os._exit + +# 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=mcs + + +[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*(# )??$ + +# 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=100 + +# 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 explicit reexports by alias from a package __init__. +allow-reexport-from-package=no + +# 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 + + +[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=arguments-differ, + broad-exception-caught, + chained-comparison, + consider-using-in, + consider-using-with, + invalid-name, + missing-class-docstring, + missing-function-docstring, + missing-module-docstring, + no-else-return, + no-member, + redefined-outer-name, + similarities, + too-many-statements, + unused-argument, + useless-return, + +# 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= + + +[METHOD_ARGS] + +# List of qualified names (i.e., library.method) which require a timeout +# parameter e.g. 'requests.api.get,requests.api.post' +timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request + + +[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 + +# Let 'consider-using-join' be raised when the separator to join on would be +# non-empty (resulting in expected fixes of the type: ``"- " + " - +# ".join(items)``) +suggest-join-with-non-empty-separator=yes + + +[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, +# json2 (improved json format), json (old json format) 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 + + +[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. No available dictionaries : You need to install +# both the python package and the system dependency for enchant to work. +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. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io diff --git a/examples/autofilter.py b/examples/autofilter.py index 924a1808c..ec51cd708 100644 --- a/examples/autofilter.py +++ b/examples/autofilter.py @@ -26,7 +26,7 @@ bold = workbook.add_format({"bold": 1}) # Open a text file with autofilter example data. -textfile = open("autofilter_data.txt") +textfile = open("autofilter_data.txt", encoding="utf-8") # Read the headers from the first line of the input file. headers = textfile.readline().strip("\n").split() diff --git a/examples/django_simple.py b/examples/django_simple.py index 75fd7d292..dd3f40d08 100644 --- a/examples/django_simple.py +++ b/examples/django_simple.py @@ -6,6 +6,8 @@ # SPDX-License-Identifier: BSD-2-Clause # Copyright 2013-2024, John McNamara, jmcnamara@cpan.org # +# pylint: skip-file + import io from django.http import HttpResponse from django.views.generic import View diff --git a/examples/dynamic_arrays.py b/examples/dynamic_arrays.py index 9bd4b24a2..182f26eb2 100644 --- a/examples/dynamic_arrays.py +++ b/examples/dynamic_arrays.py @@ -26,8 +26,8 @@ def main(): worksheet9 = workbook.add_worksheet("Spill ranges") worksheet10 = workbook.add_worksheet("Older functions") - header1 = workbook.add_format({"fg_color": "#74AC4C", "color": "#FFFFFF"}) - header2 = workbook.add_format({"fg_color": "#528FD3", "color": "#FFFFFF"}) + header1 = workbook.add_format({"fg_color": "#74AC4C", "font_color": "#FFFFFF"}) + header2 = workbook.add_format({"fg_color": "#528FD3", "font_color": "#FFFFFF"}) # # Example of using the FILTER() function. diff --git a/examples/inheritance1.py b/examples/inheritance1.py index 0622c2698..9031512fe 100644 --- a/examples/inheritance1.py +++ b/examples/inheritance1.py @@ -28,7 +28,7 @@ def write(self, row, col, *args): return self.write_string(row, col, data) else: # Call the parent version of write() as usual for other data. - return super(MyWorksheet, self).write(row, col, *args) + return super().write(row, col, *args) class MyWorkbook(Workbook): @@ -40,7 +40,7 @@ class MyWorkbook(Workbook): def add_worksheet(self, name=None): # Overwrite add_worksheet() to create a MyWorksheet object. - worksheet = super(MyWorkbook, self).add_worksheet(name, MyWorksheet) + worksheet = super().add_worksheet(name, MyWorksheet) return worksheet diff --git a/examples/inheritance2.py b/examples/inheritance2.py index ed392e665..7f4a1b6f7 100644 --- a/examples/inheritance2.py +++ b/examples/inheritance2.py @@ -19,14 +19,14 @@ from xlsxwriter.worksheet import convert_cell_args -def excel_string_width(str): +def excel_string_width(string): """ Calculate the length of the string in Excel character units. This is only an example and won't give accurate results. It will need to be replaced by something more rigorous. """ - string_width = len(str) + string_width = len(string) if string_width == 0: return 0 @@ -63,7 +63,7 @@ def write_string(self, row, col, string, cell_format=None): self.max_column_widths[col] = string_width # Now call the parent version of write_string() as usual. - return super(MyWorksheet, self).write_string(row, col, string, cell_format) + return super().write_string(row, col, string, cell_format) class MyWorkbook(Workbook): @@ -76,7 +76,7 @@ class MyWorkbook(Workbook): def add_worksheet(self, name=None): # Overwrite add_worksheet() to create a MyWorksheet object. # Also add an Worksheet attribute to store the column widths. - worksheet = super(MyWorkbook, self).add_worksheet(name, MyWorksheet) + worksheet = super().add_worksheet(name, MyWorksheet) worksheet.max_column_widths = {} return worksheet @@ -90,7 +90,7 @@ def close(self): for column, width in worksheet.max_column_widths.items(): worksheet.set_column(column, column, width) - return super(MyWorkbook, self).close() + return super().close() # Create a new MyWorkbook object. diff --git a/examples/pandas_chart_line.py b/examples/pandas_chart_line.py index b38cd11b6..0883ea631 100644 --- a/examples/pandas_chart_line.py +++ b/examples/pandas_chart_line.py @@ -7,8 +7,8 @@ # Copyright 2013-2024, John McNamara, jmcnamara@cpan.org # -import pandas as pd import random +import pandas as pd # Create some sample data to plot. categories = ["Node 1", "Node 2", "Node 3", "Node 4"] diff --git a/examples/pandas_datetime.py b/examples/pandas_datetime.py index 2e292e758..ba3c4623f 100644 --- a/examples/pandas_datetime.py +++ b/examples/pandas_datetime.py @@ -7,8 +7,8 @@ # Copyright 2013-2024, John McNamara, jmcnamara@cpan.org # -import pandas as pd from datetime import datetime, date +import pandas as pd # Create a Pandas dataframe from some datetime data. df = pd.DataFrame( diff --git a/examples/polars_chart.py b/examples/polars_chart.py index 793fadf63..ed8aa6eb2 100644 --- a/examples/polars_chart.py +++ b/examples/polars_chart.py @@ -7,8 +7,8 @@ # Copyright 2013-2024, John McNamara, jmcnamara@cpan.org # -import xlsxwriter import polars as pl +import xlsxwriter df = pl.DataFrame({"Data": [10, 20, 30, 20, 15, 30, 45]}) diff --git a/examples/polars_multiple.py b/examples/polars_multiple.py index 5d935ad78..c221ca781 100644 --- a/examples/polars_multiple.py +++ b/examples/polars_multiple.py @@ -7,8 +7,8 @@ # Copyright 2013-2024, John McNamara, jmcnamara@cpan.org # -import xlsxwriter import polars as pl +import xlsxwriter # Create some Polars dataframes from some data. df1 = pl.DataFrame({"Data": [11, 12, 13, 14]}) diff --git a/examples/polars_positioning.py b/examples/polars_positioning.py index 7fc57e7d1..7d26ec88e 100644 --- a/examples/polars_positioning.py +++ b/examples/polars_positioning.py @@ -7,8 +7,8 @@ # Copyright 2013-2024, John McNamara, jmcnamara@cpan.org # -import xlsxwriter import polars as pl +import xlsxwriter # Create some Polars dataframes from some data. df1 = pl.DataFrame({"Data": [11, 12, 13, 14]}) @@ -28,5 +28,5 @@ # Write the dataframe without the header. df4.write_excel( - workbook=workbook, worksheet="Sheet1", position="C8", has_header=False + workbook=workbook, worksheet="Sheet1", position="C8", include_header=False ) diff --git a/examples/polars_sparklines.py b/examples/polars_sparklines.py index a689cffe0..4abc4dd03 100644 --- a/examples/polars_sparklines.py +++ b/examples/polars_sparklines.py @@ -8,7 +8,6 @@ # import polars as pl -from polars.datatypes import INTEGER_DTYPES df = pl.DataFrame( { @@ -26,7 +25,7 @@ # Set an alternative table style. table_style="Table Style Light 2", # Specify an Excel number format for integer types. - dtype_formats={INTEGER_DTYPES: "#,##0_);(#,##0)"}, + dtype_formats={pl.Int32: "#,##0_);(#,##0)"}, # Configure sparklines to the dataframe. sparklines={ # We use the default options with just the source columns. diff --git a/examples/polars_xlsxwriter.py b/examples/polars_xlsxwriter.py index be1cb1a0a..396b23528 100644 --- a/examples/polars_xlsxwriter.py +++ b/examples/polars_xlsxwriter.py @@ -6,8 +6,8 @@ # Copyright 2013-2024, John McNamara, jmcnamara@cpan.org # -import xlsxwriter import polars as pl +import xlsxwriter with xlsxwriter.Workbook("polars_xlsxwriter.xlsx") as workbook: # Create a new worksheet. diff --git a/examples/rich_strings.py b/examples/rich_strings.py index c8df10c32..685189ae9 100644 --- a/examples/rich_strings.py +++ b/examples/rich_strings.py @@ -16,8 +16,8 @@ # Set up some formats to use. bold = workbook.add_format({"bold": True}) italic = workbook.add_format({"italic": True}) -red = workbook.add_format({"color": "red"}) -blue = workbook.add_format({"color": "blue"}) +red = workbook.add_format({"font_color": "red"}) +blue = workbook.add_format({"font_color": "blue"}) center = workbook.add_format({"align": "center"}) superscript = workbook.add_format({"font_script": 1}) diff --git a/examples/unicode_polish_utf8.py b/examples/unicode_polish_utf8.py index a1d58101a..de04e9416 100644 --- a/examples/unicode_polish_utf8.py +++ b/examples/unicode_polish_utf8.py @@ -12,27 +12,27 @@ import xlsxwriter # Open the input file with the correct encoding. -textfile = open("unicode_polish_utf8.txt", mode="r", encoding="utf-8") +with open("unicode_polish_utf8.txt", mode="r", encoding="utf-8") as textfile: -# Create an new Excel file and convert the text data. -workbook = xlsxwriter.Workbook("unicode_polish_utf8.xlsx") -worksheet = workbook.add_worksheet() + # Create an new Excel file and convert the text data. + workbook = xlsxwriter.Workbook("unicode_polish_utf8.xlsx") + worksheet = workbook.add_worksheet() -# Widen the first column to make the text clearer. -worksheet.set_column("A:A", 50) + # Widen the first column to make the text clearer. + worksheet.set_column("A:A", 50) -# Start from the first cell. -row = 0 -col = 0 + # Start from the first cell. + row = 0 + col = 0 -# Read the text file and write it to the worksheet. -for line in textfile: - # Ignore the comments in the text file. - if line.startswith("#"): - continue + # Read the text file and write it to the worksheet. + for line in textfile: + # Ignore the comments in the text file. + if line.startswith("#"): + continue - # Write any other lines to the worksheet. - worksheet.write(row, col, line.rstrip("\n")) - row += 1 + # Write any other lines to the worksheet. + worksheet.write(row, col, line.rstrip("\n")) + row += 1 -workbook.close() + workbook.close() diff --git a/examples/unicode_shift_jis.py b/examples/unicode_shift_jis.py index 44804af10..386c5bc82 100644 --- a/examples/unicode_shift_jis.py +++ b/examples/unicode_shift_jis.py @@ -12,27 +12,27 @@ import xlsxwriter # Open the input file with the correct encoding. -textfile = open("unicode_shift_jis.txt", mode="r", encoding="shift_jis") +with open("unicode_shift_jis.txt", mode="r", encoding="shift_jis") as textfile: -# Create an new Excel file and convert the text data. -workbook = xlsxwriter.Workbook("unicode_shift_jis.xlsx") -worksheet = workbook.add_worksheet() + # Create an new Excel file and convert the text data. + workbook = xlsxwriter.Workbook("unicode_shift_jis.xlsx") + worksheet = workbook.add_worksheet() -# Widen the first column to make the text clearer. -worksheet.set_column("A:A", 50) + # Widen the first column to make the text clearer. + worksheet.set_column("A:A", 50) -# Start from the first cell. -row = 0 -col = 0 + # Start from the first cell. + row = 0 + col = 0 -# Read the text file and write it to the worksheet. -for line in textfile: - # Ignore the comments in the text file. - if line.startswith("#"): - continue + # Read the text file and write it to the worksheet. + for line in textfile: + # Ignore the comments in the text file. + if line.startswith("#"): + continue - # Write any other lines to the worksheet. - worksheet.write(row, col, line.rstrip("\n")) - row += 1 + # Write any other lines to the worksheet. + worksheet.write(row, col, line.rstrip("\n")) + row += 1 -workbook.close() + workbook.close() diff --git a/examples/user_types1.py b/examples/user_types1.py index abb2f2783..bc923a6d6 100644 --- a/examples/user_types1.py +++ b/examples/user_types1.py @@ -6,8 +6,8 @@ # SPDX-License-Identifier: BSD-2-Clause # Copyright 2013-2024, John McNamara, jmcnamara@cpan.org # -import xlsxwriter import uuid +import xlsxwriter # Create a function that will behave like a worksheet write() method. @@ -17,8 +17,8 @@ # write_*() method. In this case it changes the UUID to a string and calls # write_string() to write it. # -def write_uuid(worksheet, row, col, token, format=None): - return worksheet.write_string(row, col, str(token), format) +def write_uuid(worksheet, row, col, token, cell_format=None): + return worksheet.write_string(row, col, str(token), cell_format) # Set up the workbook as usual. diff --git a/examples/user_types2.py b/examples/user_types2.py index c86423393..763807538 100644 --- a/examples/user_types2.py +++ b/examples/user_types2.py @@ -6,8 +6,8 @@ # SPDX-License-Identifier: BSD-2-Clause # Copyright 2013-2024, John McNamara, jmcnamara@cpan.org # -import xlsxwriter import math +import xlsxwriter # Create a function that will behave like a worksheet write() method. @@ -16,9 +16,9 @@ # instead. It should take the parameters shown below and return the return # value from the called worksheet write_*() method. # -def ignore_nan(worksheet, row, col, number, format=None): +def ignore_nan(worksheet, row, col, number, cell_format=None): if math.isnan(number): - return worksheet.write_blank(row, col, None, format) + return worksheet.write_blank(row, col, None, cell_format) else: # Return control to the calling write() method for any other number. return None diff --git a/examples/user_types3.py b/examples/user_types3.py index 495c4ddfc..815a95c72 100644 --- a/examples/user_types3.py +++ b/examples/user_types3.py @@ -13,11 +13,11 @@ # hides/replaces user passwords when writing string data. The password data, # based on the sample data structure, will be data in the second column, apart # from the header row. -def hide_password(worksheet, row, col, string, format=None): +def hide_password(worksheet, row, col, string, cell_format=None): if col == 1 and row > 0: - return worksheet.write_string(row, col, "****", format) + return worksheet.write_string(row, col, "****", cell_format) else: - return worksheet.write_string(row, col, string, format) + return worksheet.write_string(row, col, string, cell_format) # Set up the workbook as usual. diff --git a/examples/vba_extract.py b/examples/vba_extract.py index 5663765cf..5ce56786e 100644 --- a/examples/vba_extract.py +++ b/examples/vba_extract.py @@ -8,6 +8,7 @@ # SPDX-License-Identifier: BSD-2-Clause # Copyright 2013-2024, John McNamara, jmcnamara@cpan.org # + import sys from zipfile import ZipFile from zipfile import BadZipFile @@ -41,7 +42,7 @@ def extract_file(xlsm_zip, filename): "\n" "Usage: vba_extract file.xlsm\n" ) - exit() + sys.exit() try: # Open the Excel xlsm file as a zip file. @@ -58,21 +59,21 @@ def extract_file(xlsm_zip, filename): except IOError as e: print(f"File error: {str(e)}") - exit() + sys.exit() except KeyError as e: # Usually when there isn't a xl/vbaProject.bin member in the file. print(f"File error: {str(e)}") print(f"File may not be an Excel xlsm macro file: '{xlsm_file}'") - exit() + sys.exit() except BadZipFile as e: # Usually if the file is an xls file and not an xlsm file. print(f"File error: {str(e)}: '{xlsm_file}'") print("File may not be an Excel xlsm macro file.") - exit() + sys.exit() except Exception as e: # Catch any other exceptions. print(f"File error: {str(e)}") - exit() + sys.exit() diff --git a/setup.py b/setup.py index f91b86abf..00064aada 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ if sys.version_info < (3, 6): warn("The minimum Python version supported by XlsxWriter is 3.6") - exit() + sys.exit() class PyTest(Command): diff --git a/xlsxwriter/app.py b/xlsxwriter/app.py index a9861dc99..dcff537ce 100644 --- a/xlsxwriter/app.py +++ b/xlsxwriter/app.py @@ -29,7 +29,7 @@ def __init__(self): """ - super(App, self).__init__() + super().__init__() self.part_names = [] self.heading_pairs = [] diff --git a/xlsxwriter/chart.py b/xlsxwriter/chart.py index 840da77c8..e106768e7 100644 --- a/xlsxwriter/chart.py +++ b/xlsxwriter/chart.py @@ -5,17 +5,18 @@ # SPDX-License-Identifier: BSD-2-Clause # Copyright 2013-2024, John McNamara, jmcnamara@cpan.org # + import re import copy from warnings import warn from .shape import Shape from . import xmlwriter -from .utility import get_rgb_color +from .utility import _get_rgb_color from .utility import xl_rowcol_to_cell from .utility import xl_range_formula -from .utility import supported_datetime -from .utility import datetime_to_excel_datetime +from .utility import _supported_datetime +from .utility import _datetime_to_excel_datetime from .utility import quote_sheetname @@ -32,13 +33,13 @@ class Chart(xmlwriter.XMLwriter): # ########################################################################### - def __init__(self, options=None): + def __init__(self): """ Constructor. """ - super(Chart, self).__init__() + super().__init__() self.subtype = None self.sheet_type = 0x0200 @@ -105,6 +106,7 @@ def __init__(self, options=None): self.is_secondary = False self.warn_sheetname = True self._set_default_properties() + self.fill = {} def add_series(self, options=None): """ @@ -765,16 +767,16 @@ def _convert_axis_args(self, axis, user_options): axis["text_axis"] = True # Convert datetime args if required. - if axis.get("min") and supported_datetime(axis["min"]): - axis["min"] = datetime_to_excel_datetime( + if axis.get("min") and _supported_datetime(axis["min"]): + axis["min"] = _datetime_to_excel_datetime( axis["min"], self.date_1904, self.remove_timezone ) - if axis.get("max") and supported_datetime(axis["max"]): - axis["max"] = datetime_to_excel_datetime( + if axis.get("max") and _supported_datetime(axis["max"]): + axis["max"] = _datetime_to_excel_datetime( axis["max"], self.date_1904, self.remove_timezone ) - if axis.get("crossing") and supported_datetime(axis["crossing"]): - axis["crossing"] = datetime_to_excel_datetime( + if axis.get("crossing") and _supported_datetime(axis["crossing"]): + axis["crossing"] = _datetime_to_excel_datetime( axis["crossing"], self.date_1904, self.remove_timezone ) @@ -817,7 +819,7 @@ def _convert_axis_args(self, axis, user_options): def _convert_font_args(self, options): # Convert user defined font values into private dict values. if not options: - return + return {} font = { "name": options.get("name"), @@ -915,7 +917,7 @@ def _get_data_id(self, formula, data): # Ignore series without a range formula. if not formula: - return + return None # Strip the leading '=' from the formula. if formula.startswith("="): @@ -943,7 +945,7 @@ def _get_marker_properties(self, marker): # Convert user marker properties to the structure required internally. if not marker: - return + return None # Copy the user defined properties since they will be modified. marker = copy.deepcopy(marker) @@ -973,7 +975,7 @@ def _get_marker_properties(self, marker): marker["type"] = types[marker_type] else: warn(f"Unknown marker type '{marker_type}") - return + return None # Set the line properties for the marker. line = Shape._get_line_properties(marker.get("line")) @@ -1011,7 +1013,7 @@ def _get_trendline_properties(self, trendline): # Convert user trendline properties to structure required internally. if not trendline: - return + return None # Copy the user defined properties since they will be modified. trendline = copy.deepcopy(trendline) @@ -1032,7 +1034,7 @@ def _get_trendline_properties(self, trendline): trendline["type"] = types[trend_type] else: warn(f"Unknown trendline type '{trend_type}'") - return + return None # Set the line properties for the trendline. line = Shape._get_line_properties(trendline.get("line")) @@ -1118,7 +1120,7 @@ def _get_trendline_label_properties(self, label): def _get_error_bars_props(self, options): # Convert user error bars properties to structure required internally. if not options: - return + return {} # Default values. error_bars = {"type": "fixedVal", "value": 1, "endcap": 1, "direction": "both"} @@ -1138,7 +1140,7 @@ def _get_error_bars_props(self, options): error_bars["type"] = types[error_type] else: warn(f"Unknown error bars type '{error_type}") - return + return {} # Set the value for error types that require it. if "value" in options: @@ -1200,7 +1202,7 @@ def _get_labels_properties(self, labels): labels["position"] = self.label_positions[position] else: warn(f"Unsupported label position '{position}' for this chart type") - return + return None # Map the user defined label separator to the Excel separator. separator = labels.get("separator") @@ -1217,7 +1219,7 @@ def _get_labels_properties(self, labels): labels["separator"] = separators[separator] else: warn("Unsupported label separator") - return + return None # Set the font properties if present. labels["font"] = self._convert_font_args(labels.get("font")) @@ -1399,7 +1401,7 @@ def _get_layout_properties(self, args, is_text): layout = {} if not args: - return + return {} if is_text: properties = ("x", "y") @@ -1410,13 +1412,13 @@ def _get_layout_properties(self, args, is_text): for key in args.keys(): if key not in properties: warn(f"Property '{key}' not supported in layout options") - return + return {} # Set the layout properties. for prop in properties: if prop not in args.keys(): warn(f"Property '{prop}' must be specified in layout options") - return + return {} value = args[prop] @@ -1424,14 +1426,14 @@ def _get_layout_properties(self, args, is_text): float(value) except ValueError: warn(f"Property '{prop}' value '{value}' must be numeric in layout") - return + return {} if value < 0 or value > 1: warn( f"Property '{prop}' value '{value}' must be in range " f"0 < x <= 1 in layout options" ) - return + return {} # Convert to the format used by Excel for easier testing layout[prop] = f"{value:.17g}" @@ -1443,7 +1445,7 @@ def _get_points_properties(self, user_points): points = [] if not user_points: - return + return [] for user_point in user_points: point = {} @@ -1496,15 +1498,12 @@ def _has_fill_formatting(self, element): if element.get("line") and element["line"]["defined"]: has_line = True - if not has_fill and not has_line and not has_pattern and not has_gradient: - return False - else: - return True + return has_fill or has_line or has_pattern or has_gradient def _get_display_units(self, display_units): # Convert user defined display units to internal units. if not display_units: - return + return None types = { "hundreds": "hundreds", @@ -1522,14 +1521,14 @@ def _get_display_units(self, display_units): display_units = types[display_units] else: warn(f"Unknown display_units type '{display_units}'") - return + return None return display_units def _get_tick_type(self, tick_type): # Convert user defined display units to internal units. if not tick_type: - return + return None types = { "outside": "out", @@ -1542,7 +1541,7 @@ def _get_tick_type(self, tick_type): tick_type = types[tick_type] else: warn(f"Unknown tick_type '{tick_type}'") - return + return None return tick_type @@ -1818,7 +1817,8 @@ def _write_manual_layout(self, layout, layout_type): self._xml_end_tag("c:manualLayout") - def _write_chart_type(self, options): + def _write_chart_type(self, args): + # pylint: disable=unused-argument # Write the chart type element. This method should be overridden # by the subclasses. return @@ -2064,7 +2064,7 @@ def _write_multi_lvl_str_ref(self, formula, data): for i, point in enumerate(cat_data): # Write the c:pt element. - self._write_pt(i, cat_data[i]) + self._write_pt(i, point) self._xml_end_tag("c:lvl") @@ -2109,7 +2109,7 @@ def _write_cat_axis(self, args): axis_ids = args["axis_ids"] # If there are no axis_ids then we don't need to write this element. - if axis_ids is None or not len(axis_ids): + if axis_ids is None or not axis_ids: return position = self.cat_axis_position @@ -2213,7 +2213,7 @@ def _write_val_axis(self, args): is_y_axis = self.horiz_val_axis # If there are no axis_ids then we don't need to write this element. - if axis_ids is None or not len(axis_ids): + if axis_ids is None or not axis_ids: return # Overwrite the default axis position with a user supplied value. @@ -2319,7 +2319,7 @@ def _write_cat_val_axis(self, args): is_y_axis = self.horiz_val_axis # If there are no axis_ids then we don't need to write this element. - if axis_ids is None or not len(axis_ids): + if axis_ids is None or not axis_ids: return # Overwrite the default axis position with a user supplied value. @@ -2419,7 +2419,7 @@ def _write_date_axis(self, args): axis_ids = args["axis_ids"] # If there are no axis_ids then we don't need to write this element. - if axis_ids is None or not len(axis_ids): + if axis_ids is None or not axis_ids: return position = self.cat_axis_position @@ -3360,7 +3360,7 @@ def _write_a_solid_fill(self, fill): self._xml_start_tag("a:solidFill") if "color" in fill: - color = get_rgb_color(fill["color"]) + color = _get_rgb_color(fill["color"]) transparency = fill.get("transparency") # Write the a:srgbClr element. self._write_a_srgb_clr(color, transparency) @@ -3462,8 +3462,7 @@ def _write_name(self, data): def _write_trendline_order(self, val): # Write the element. - if val < 2: - val = 2 + val = max(val, 2) attributes = [("val", val)] @@ -3471,8 +3470,7 @@ def _write_trendline_order(self, val): def _write_period(self, val): # Write the element. - if val < 2: - val = 2 + val = max(val, 2) attributes = [("val", val)] @@ -4273,13 +4271,13 @@ def _write_a_gs_lst(self, gradient): self._xml_start_tag("a:gsLst") - for i in range(len(colors)): + for i, color in enumerate(colors): pos = int(positions[i] * 1000) attributes = [("pos", pos)] self._xml_start_tag("a:gs", attributes) # Write the a:srgbClr element. - color = get_rgb_color(colors[i]) + color = _get_rgb_color(color) self._write_a_srgb_clr(color) self._xml_end_tag("a:gs") @@ -4359,7 +4357,7 @@ def _write_a_patt_fill(self, pattern): def _write_a_fg_clr(self, color): # Write the element. - color = get_rgb_color(color) + color = _get_rgb_color(color) self._xml_start_tag("a:fgClr") @@ -4371,7 +4369,7 @@ def _write_a_fg_clr(self, color): def _write_a_bg_clr(self, color): # Write the element. - color = get_rgb_color(color) + color = _get_rgb_color(color) self._xml_start_tag("a:bgClr") diff --git a/xlsxwriter/chart_area.py b/xlsxwriter/chart_area.py index 61d05dcf5..6c0fad713 100644 --- a/xlsxwriter/chart_area.py +++ b/xlsxwriter/chart_area.py @@ -27,7 +27,7 @@ def __init__(self, options=None): Constructor. """ - super(ChartArea, self).__init__() + super().__init__() if options is None: options = {} @@ -75,7 +75,7 @@ def _write_area_chart(self, args): else: series = self._get_secondary_axes_series() - if not len(series): + if not series: return subtype = self.subtype diff --git a/xlsxwriter/chart_bar.py b/xlsxwriter/chart_bar.py index 4c689f2ca..3ef06c473 100644 --- a/xlsxwriter/chart_bar.py +++ b/xlsxwriter/chart_bar.py @@ -6,8 +6,8 @@ # Copyright 2013-2024, John McNamara, jmcnamara@cpan.org # -from . import chart from warnings import warn +from . import chart class ChartBar(chart.Chart): @@ -28,7 +28,7 @@ def __init__(self, options=None): Constructor. """ - super(ChartBar, self).__init__() + super().__init__() if options is None: options = {} @@ -64,6 +64,7 @@ def __init__(self, options=None): self.set_y_axis({}) def combine(self, chart=None): + # pylint: disable=redefined-outer-name """ Create a combination chart with a secondary chart. @@ -114,7 +115,7 @@ def _write_bar_chart(self, args): else: series = self._get_secondary_axes_series() - if not len(series): + if not series: return subtype = self.subtype diff --git a/xlsxwriter/chart_column.py b/xlsxwriter/chart_column.py index 3d036521e..dbcc1a1c8 100644 --- a/xlsxwriter/chart_column.py +++ b/xlsxwriter/chart_column.py @@ -27,7 +27,7 @@ def __init__(self, options=None): Constructor. """ - super(ChartColumn, self).__init__() + super().__init__() if options is None: options = {} @@ -73,7 +73,7 @@ def _write_bar_chart(self, args): else: series = self._get_secondary_axes_series() - if not len(series): + if not series: return subtype = self.subtype diff --git a/xlsxwriter/chart_doughnut.py b/xlsxwriter/chart_doughnut.py index 82c712d42..bab3c1b0f 100644 --- a/xlsxwriter/chart_doughnut.py +++ b/xlsxwriter/chart_doughnut.py @@ -23,12 +23,12 @@ class ChartDoughnut(chart_pie.ChartPie): # ########################################################################### - def __init__(self, options=None): + def __init__(self): """ Constructor. """ - super(ChartDoughnut, self).__init__() + super().__init__() self.vary_data_color = 1 self.rotation = 0 @@ -64,7 +64,7 @@ def set_hole_size(self, size): def _write_chart_type(self, args): # Override the virtual superclass method with a chart specific method. # Write the c:doughnutChart element. - self._write_doughnut_chart(args) + self._write_doughnut_chart() ########################################################################### # @@ -72,7 +72,7 @@ def _write_chart_type(self, args): # ########################################################################### - def _write_doughnut_chart(self, args): + def _write_doughnut_chart(self): # Write the element. Over-ridden method to remove # axis_id code since Doughnut charts don't require val and cat axes. self._xml_start_tag("c:doughnutChart") diff --git a/xlsxwriter/chart_line.py b/xlsxwriter/chart_line.py index 5fdc81e79..a1fde491b 100644 --- a/xlsxwriter/chart_line.py +++ b/xlsxwriter/chart_line.py @@ -27,7 +27,7 @@ def __init__(self, options=None): Constructor. """ - super(ChartLine, self).__init__() + super().__init__() if options is None: options = {} @@ -84,7 +84,7 @@ def _write_line_chart(self, args): else: series = self._get_secondary_axes_series() - if not len(series): + if not series: return subtype = self.subtype diff --git a/xlsxwriter/chart_pie.py b/xlsxwriter/chart_pie.py index 61b9bb630..e41259921 100644 --- a/xlsxwriter/chart_pie.py +++ b/xlsxwriter/chart_pie.py @@ -23,12 +23,12 @@ class ChartPie(chart.Chart): # ########################################################################### - def __init__(self, options=None): + def __init__(self): """ Constructor. """ - super(ChartPie, self).__init__() + super().__init__() self.vary_data_color = 1 self.rotation = 0 @@ -74,7 +74,7 @@ def set_rotation(self, rotation): def _write_chart_type(self, args): # Override the virtual superclass method with a chart specific method. # Write the c:pieChart element. - self._write_pie_chart(args) + self._write_pie_chart() ########################################################################### # @@ -82,7 +82,7 @@ def _write_chart_type(self, args): # ########################################################################### - def _write_pie_chart(self, args): + def _write_pie_chart(self): # Write the element. Over-ridden method to remove # axis_id code since Pie charts don't require val and cat axes. self._xml_start_tag("c:pieChart") @@ -129,6 +129,7 @@ def _write_plot_area(self): second_chart.series_index = self.series_index # Write the subclass chart type elements for combined chart. + # pylint: disable-next=protected-access second_chart._write_chart_type(None) # Write the c:spPr element for the plotarea formatting. diff --git a/xlsxwriter/chart_radar.py b/xlsxwriter/chart_radar.py index bc6194ed0..7be798379 100644 --- a/xlsxwriter/chart_radar.py +++ b/xlsxwriter/chart_radar.py @@ -27,7 +27,7 @@ def __init__(self, options=None): Constructor. """ - super(ChartRadar, self).__init__() + super().__init__() if options is None: options = {} @@ -73,7 +73,7 @@ def _write_radar_chart(self, args): else: series = self._get_secondary_axes_series() - if not len(series): + if not series: return self._xml_start_tag("c:radarChart") diff --git a/xlsxwriter/chart_scatter.py b/xlsxwriter/chart_scatter.py index 24fba2828..056f25f9a 100644 --- a/xlsxwriter/chart_scatter.py +++ b/xlsxwriter/chart_scatter.py @@ -6,8 +6,8 @@ # Copyright 2013-2024, John McNamara, jmcnamara@cpan.org # -from . import chart from warnings import warn +from . import chart class ChartScatter(chart.Chart): @@ -28,7 +28,7 @@ def __init__(self, options=None): Constructor. """ - super(ChartScatter, self).__init__() + super().__init__() if options is None: options = {} @@ -58,6 +58,7 @@ def __init__(self, options=None): } def combine(self, chart=None): + # pylint: disable=redefined-outer-name """ Create a combination chart with a secondary chart. @@ -103,7 +104,7 @@ def _write_scatter_chart(self, args): else: series = self._get_secondary_axes_series() - if not len(series): + if not series: return style = "lineMarker" diff --git a/xlsxwriter/chart_stock.py b/xlsxwriter/chart_stock.py index 0cc23e06e..9aab350e1 100644 --- a/xlsxwriter/chart_stock.py +++ b/xlsxwriter/chart_stock.py @@ -21,12 +21,12 @@ class ChartStock(chart.Chart): # ########################################################################### - def __init__(self, options=None): + def __init__(self): """ Constructor. """ - super(ChartStock, self).__init__() + super().__init__() self.show_crosses = False self.hi_low_lines = {} @@ -78,7 +78,7 @@ def _write_stock_chart(self, args): else: series = self._get_secondary_axes_series() - if not len(series): + if not series: return # Add default formatting to the series data. diff --git a/xlsxwriter/chartsheet.py b/xlsxwriter/chartsheet.py index d0d574df7..ab47827b7 100644 --- a/xlsxwriter/chartsheet.py +++ b/xlsxwriter/chartsheet.py @@ -29,7 +29,7 @@ def __init__(self): """ - super(Chartsheet, self).__init__() + super().__init__() self.is_chartsheet = True self.drawing = None @@ -98,7 +98,7 @@ def protect(self, password="", options=None): self.protection = True # Call the parent method. - super(Chartsheet, self).protect(password, copy) + super().protect(password, copy) ########################################################################### # diff --git a/xlsxwriter/comments.py b/xlsxwriter/comments.py index 02101d411..a7df89c0b 100644 --- a/xlsxwriter/comments.py +++ b/xlsxwriter/comments.py @@ -7,7 +7,7 @@ # from . import xmlwriter -from .utility import preserve_whitespace +from .utility import _preserve_whitespace from .utility import xl_rowcol_to_cell @@ -30,7 +30,7 @@ def __init__(self): """ - super(Comments, self).__init__() + super().__init__() self.author_ids = {} ########################################################################### @@ -39,9 +39,12 @@ def __init__(self): # ########################################################################### - def _assemble_xml_file(self, comments_data=[]): + def _assemble_xml_file(self, comments_data=None): # Assemble and write the XML file. + if comments_data is None: + comments_data = [] + # Write the XML declaration. self._xml_declaration() @@ -161,7 +164,7 @@ def _write_text_t(self, text): # Write the text element. attributes = [] - if preserve_whitespace(text): + if _preserve_whitespace(text): attributes.append(("xml:space", "preserve")) self._xml_data_element("t", text, attributes) diff --git a/xlsxwriter/contenttypes.py b/xlsxwriter/contenttypes.py index 6e27e48a9..c7a69e124 100644 --- a/xlsxwriter/contenttypes.py +++ b/xlsxwriter/contenttypes.py @@ -10,20 +10,20 @@ from . import xmlwriter # Long namespace strings used in the class. -app_package = "application/vnd.openxmlformats-package." -app_document = "application/vnd.openxmlformats-officedocument." +APP_PACKAGE = "application/vnd.openxmlformats-package." +APP_DOCUMENT = "application/vnd.openxmlformats-officedocument." defaults = [ - ["rels", app_package + "relationships+xml"], + ["rels", APP_PACKAGE + "relationships+xml"], ["xml", "application/xml"], ] overrides = [ - ["/docProps/app.xml", app_document + "extended-properties+xml"], - ["/docProps/core.xml", app_package + "core-properties+xml"], - ["/xl/styles.xml", app_document + "spreadsheetml.styles+xml"], - ["/xl/theme/theme1.xml", app_document + "theme+xml"], - ["/xl/workbook.xml", app_document + "spreadsheetml.sheet.main+xml"], + ["/docProps/app.xml", APP_DOCUMENT + "extended-properties+xml"], + ["/docProps/core.xml", APP_PACKAGE + "core-properties+xml"], + ["/xl/styles.xml", APP_DOCUMENT + "spreadsheetml.styles+xml"], + ["/xl/theme/theme1.xml", APP_DOCUMENT + "theme+xml"], + ["/xl/workbook.xml", APP_DOCUMENT + "spreadsheetml.sheet.main+xml"], ] @@ -46,7 +46,7 @@ def __init__(self): """ - super(ContentTypes, self).__init__() + super().__init__() # Copy the defaults in case we need to change them. self.defaults = copy.deepcopy(defaults) @@ -86,7 +86,7 @@ def _add_worksheet_name(self, worksheet_name): worksheet_name = "/xl/worksheets/" + worksheet_name + ".xml" self._add_override( - (worksheet_name, app_document + "spreadsheetml.worksheet+xml") + (worksheet_name, APP_DOCUMENT + "spreadsheetml.worksheet+xml") ) def _add_chartsheet_name(self, chartsheet_name): @@ -94,41 +94,41 @@ def _add_chartsheet_name(self, chartsheet_name): chartsheet_name = "/xl/chartsheets/" + chartsheet_name + ".xml" self._add_override( - (chartsheet_name, app_document + "spreadsheetml.chartsheet+xml") + (chartsheet_name, APP_DOCUMENT + "spreadsheetml.chartsheet+xml") ) def _add_chart_name(self, chart_name): # Add the name of a chart to the ContentTypes overrides. chart_name = "/xl/charts/" + chart_name + ".xml" - self._add_override((chart_name, app_document + "drawingml.chart+xml")) + self._add_override((chart_name, APP_DOCUMENT + "drawingml.chart+xml")) def _add_drawing_name(self, drawing_name): # Add the name of a drawing to the ContentTypes overrides. drawing_name = "/xl/drawings/" + drawing_name + ".xml" - self._add_override((drawing_name, app_document + "drawing+xml")) + self._add_override((drawing_name, APP_DOCUMENT + "drawing+xml")) def _add_vml_name(self): # Add the name of a VML drawing to the ContentTypes defaults. - self._add_default(("vml", app_document + "vmlDrawing")) + self._add_default(("vml", APP_DOCUMENT + "vmlDrawing")) def _add_comment_name(self, comment_name): # Add the name of a comment to the ContentTypes overrides. comment_name = "/xl/" + comment_name + ".xml" - self._add_override((comment_name, app_document + "spreadsheetml.comments+xml")) + self._add_override((comment_name, APP_DOCUMENT + "spreadsheetml.comments+xml")) def _add_shared_strings(self): # Add the sharedStrings link to the ContentTypes overrides. self._add_override( - ("/xl/sharedStrings.xml", app_document + "spreadsheetml.sharedStrings+xml") + ("/xl/sharedStrings.xml", APP_DOCUMENT + "spreadsheetml.sharedStrings+xml") ) def _add_calc_chain(self): # Add the calcChain link to the ContentTypes overrides. self._add_override( - ("/xl/calcChain.xml", app_document + "spreadsheetml.calcChain+xml") + ("/xl/calcChain.xml", APP_DOCUMENT + "spreadsheetml.calcChain+xml") ) def _add_image_types(self, image_types): @@ -145,7 +145,7 @@ def _add_table_name(self, table_name): # Add the name of a table to the ContentTypes overrides. table_name = "/xl/tables/" + table_name + ".xml" - self._add_override((table_name, app_document + "spreadsheetml.table+xml")) + self._add_override((table_name, APP_DOCUMENT + "spreadsheetml.table+xml")) def _add_vba_project(self): # Add a vbaProject to the ContentTypes defaults. @@ -170,13 +170,13 @@ def _add_vba_project_signature(self): def _add_custom_properties(self): # Add the custom properties to the ContentTypes overrides. self._add_override( - ("/docProps/custom.xml", app_document + "custom-properties+xml") + ("/docProps/custom.xml", APP_DOCUMENT + "custom-properties+xml") ) def _add_metadata(self): # Add the metadata file to the ContentTypes overrides. self._add_override( - ("/xl/metadata.xml", app_document + "spreadsheetml.sheetMetadata+xml") + ("/xl/metadata.xml", APP_DOCUMENT + "spreadsheetml.sheetMetadata+xml") ) def _add_rich_value(self): diff --git a/xlsxwriter/core.py b/xlsxwriter/core.py index d736057a4..24e8ba5e9 100644 --- a/xlsxwriter/core.py +++ b/xlsxwriter/core.py @@ -32,7 +32,7 @@ def __init__(self): """ - super(Core, self).__init__() + super().__init__() self.properties = {} diff --git a/xlsxwriter/custom.py b/xlsxwriter/custom.py index dd3fa9b50..f4b4b40ee 100644 --- a/xlsxwriter/custom.py +++ b/xlsxwriter/custom.py @@ -29,7 +29,7 @@ def __init__(self): """ - super(Custom, self).__init__() + super().__init__() self.properties = [] self.pid = 1 diff --git a/xlsxwriter/drawing.py b/xlsxwriter/drawing.py index 4f660c3f4..fd25a55d8 100644 --- a/xlsxwriter/drawing.py +++ b/xlsxwriter/drawing.py @@ -8,7 +8,7 @@ from . import xmlwriter from .shape import Shape -from .utility import get_rgb_color +from .utility import _get_rgb_color class Drawing(xmlwriter.XMLwriter): @@ -30,7 +30,7 @@ def __init__(self): """ - super(Drawing, self).__init__() + super().__init__() self.drawings = [] self.embedded = 0 @@ -519,9 +519,7 @@ def _write_sp( self._write_nv_cxn_sp_pr(index, shape) # Write the xdr:spPr element. - self._write_xdr_sp_pr( - index, col_absolute, row_absolute, width, height, shape - ) + self._write_xdr_sp_pr(col_absolute, row_absolute, width, height, shape) self._xml_end_tag("xdr:cxnSp") else: @@ -536,16 +534,14 @@ def _write_sp( ) # Write the xdr:spPr element. - self._write_xdr_sp_pr( - index, col_absolute, row_absolute, width, height, shape - ) + self._write_xdr_sp_pr(col_absolute, row_absolute, width, height, shape) # Write the xdr:style element. self._write_style() # Write the xdr:txBody element. if shape.text is not None: - self._write_tx_body(col_absolute, row_absolute, width, height, shape) + self._write_tx_body(shape) self._xml_end_tag("xdr:sp") @@ -612,9 +608,7 @@ def _write_pic( self._xml_start_tag("xdr:pic") # Write the xdr:nvPicPr element. - self._write_nv_pic_pr( - index, rel_index, description, url_rel_index, tip, decorative - ) + self._write_nv_pic_pr(index, description, url_rel_index, tip, decorative) # Write the xdr:blipFill element. self._write_blip_fill(rel_index) @@ -623,9 +617,7 @@ def _write_pic( self._xml_end_tag("xdr:pic") - def _write_nv_pic_pr( - self, index, rel_index, description, url_rel_index, tip, decorative - ): + def _write_nv_pic_pr(self, index, description, url_rel_index, tip, decorative): # Write the element. self._xml_start_tag("xdr:nvPicPr") @@ -707,7 +699,7 @@ def _write_sp_pr(self, col_absolute, row_absolute, width, height, shape=None): self._xml_end_tag("xdr:spPr") - def _write_xdr_sp_pr(self, index, col_absolute, row_absolute, width, height, shape): + def _write_xdr_sp_pr(self, col_absolute, row_absolute, width, height, shape): # Write the element for shapes. self._xml_start_tag("xdr:spPr") @@ -726,7 +718,7 @@ def _write_xdr_sp_pr(self, index, col_absolute, row_absolute, width, height, sha self._xml_empty_tag("a:noFill") elif "color" in shape.fill: # Write the a:solidFill element. - self._write_a_solid_fill(get_rgb_color(shape.fill["color"])) + self._write_a_solid_fill(_get_rgb_color(shape.fill["color"])) if shape.gradient: # Write the a:gradFill element. @@ -867,7 +859,7 @@ def _write_a_ln(self, line): elif "color" in line: # Write the a:solidFill element. - self._write_a_solid_fill(get_rgb_color(line["color"])) + self._write_a_solid_fill(_get_rgb_color(line["color"])) else: # Write the a:solidFill element. @@ -881,7 +873,7 @@ def _write_a_ln(self, line): self._xml_end_tag("a:ln") - def _write_tx_body(self, col_absolute, row_absolute, width, height, shape): + def _write_tx_body(self, shape): # Write the element. attributes = [] @@ -927,6 +919,7 @@ def _write_tx_body(self, col_absolute, row_absolute, width, height, shape): # Set the font attributes. font = shape.font + # pylint: disable=protected-access style_attrs = Shape._get_font_style_attributes(font) latin_attrs = Shape._get_font_latin_attributes(font) style_attrs.insert(0, ("lang", font["lang"])) @@ -956,7 +949,8 @@ def _write_tx_body(self, col_absolute, row_absolute, width, height, shape): self._write_font_run(font, style_attrs, latin_attrs, "a:endParaRPr") self._xml_end_tag("a:p") continue - elif "text" in shape.align: + + if "text" in shape.align: if shape.align["text"] == "left": self._xml_empty_tag("a:pPr", [("algn", "l")]) if shape.align["text"] == "center": @@ -977,16 +971,13 @@ def _write_tx_body(self, col_absolute, row_absolute, width, height, shape): def _write_font_run(self, font, style_attrs, latin_attrs, run_type): # Write a:rPr or a:endParaRPr. - if font.get("color") is not None: - has_color = True - else: - has_color = False + has_color = font.get("color") is not None if latin_attrs or has_color: self._xml_start_tag(run_type, style_attrs) if has_color: - self._write_a_solid_fill(get_rgb_color(font["color"])) + self._write_a_solid_fill(_get_rgb_color(font["color"])) if latin_attrs: self._write_a_latin(latin_attrs) @@ -1120,13 +1111,13 @@ def _write_a_gs_lst(self, gradient): self._xml_start_tag("a:gsLst") - for i in range(len(colors)): + for i, color in enumerate(colors): pos = int(positions[i] * 1000) attributes = [("pos", pos)] self._xml_start_tag("a:gs", attributes) # Write the a:srgbClr element. - color = get_rgb_color(colors[i]) + color = _get_rgb_color(color) self._write_a_srgb_clr(color) self._xml_end_tag("a:gs") diff --git a/xlsxwriter/format.py b/xlsxwriter/format.py index 63e81cf28..9abba9518 100644 --- a/xlsxwriter/format.py +++ b/xlsxwriter/format.py @@ -7,8 +7,8 @@ # # Package imports. -from . import xmlwriter from warnings import warn +from . import xmlwriter class Format(xmlwriter.XMLwriter): @@ -32,7 +32,7 @@ def __init__(self, properties=None, xf_indices=None, dxf_indices=None): if properties is None: properties = {} - super(Format, self).__init__() + super().__init__() self.xf_format_indices = xf_indices self.dxf_format_indices = dxf_indices @@ -342,6 +342,7 @@ def set_align(self, alignment): self.set_text_v_align(5) def set_center_across(self, align_type=None): + # pylint: disable=unused-argument """ Set the Format center_across property. @@ -665,99 +666,262 @@ def set_quote_prefix(self, quote_prefix=True): ########################################################################### def set_has_font(self, has_font=True): - # Set the has_font property. + """ + Set the property to indicate the format has a font. + + Args: + has_font: Default is True, turns property on. + + Returns: + Nothing. + + """ self.has_font = has_font def set_has_fill(self, has_fill=True): - # Set the has_fill property. + """ + Set the property to indicate the format has a fill. + + Args: + has_fill: Default is True, turns property on. + + Returns: + Nothing. + + """ self.has_fill = has_fill def set_font_index(self, font_index): - # Set the font_index property. + """ + Set the unique font index property. + + Args: + font_index: The unique font index. + + Returns: + Nothing. + + """ self.font_index = font_index def set_xf_index(self, xf_index): - # Set the xf_index property. + """ + Set the unique format index property. + + Args: + xf_index: The unique Excel format index. + + Returns: + Nothing. + + """ self.xf_index = xf_index def set_dxf_index(self, dxf_index): - # Set the xf_index property. + """ + Set the unique conditional format index property. + + Args: + dxf_index: The unique Excel conditional format index. + + Returns: + Nothing. + + """ self.dxf_index = dxf_index def set_num_format_index(self, num_format_index): - # Set the num_format_index property. + """ + Set the number format_index property. + + Args: + num_format_index: The unique number format index. + + Returns: + Nothing. + + """ self.num_format_index = num_format_index def set_text_h_align(self, text_h_align): - # Set the text_h_align property. + """ + Set the horizontal text alignment property. + + Args: + text_h_align: Horizontal text alignment. + + Returns: + Nothing. + + """ self.text_h_align = text_h_align def set_text_v_align(self, text_v_align): - # Set the text_v_align property. + """ + Set the vertical text alignment property. + + Args: + text_h_align: Vertical text alignment. + + Returns: + Nothing. + + """ self.text_v_align = text_v_align def set_reading_order(self, direction=0): # Set the reading_order property. + """ + Set the reading order property. + + Args: + direction: Default is 0, left to right. + + Returns: + Nothing. + + """ self.reading_order = direction def set_valign(self, align): # Set vertical cell alignment. This is required by the constructor # properties dict to differentiate between the vertical and horizontal # properties. + """ + Set vertical cell alignment property. + + This is required by the constructor properties dict to differentiate + between the vertical and horizontal properties. + + Args: + align: Alignment property. + + Returns: + Nothing. + + """ self.set_align(align) def set_font_family(self, font_family): - # Set the Format font_family property. + """ + Set the font family property. + + Args: + font_family: Font family number. + + Returns: + Nothing. + + """ self.font_family = font_family def set_font_charset(self, font_charset): - # Set the Format font_charset property. + """ + Set the font character set property. + + Args: + font_charset: The font character set number. + + Returns: + Nothing. + + """ self.font_charset = font_charset def set_font_scheme(self, font_scheme): - # Set the Format font_scheme property. + """ + Set the font scheme property. + + Args: + font_scheme: The font scheme. + + Returns: + Nothing. + + """ self.font_scheme = font_scheme def set_font_condense(self, font_condense): - # Set the Format font_condense property. + """ + Set the font condense property. + + Args: + font_condense: The font condense property. + + Returns: + Nothing. + + """ self.font_condense = font_condense def set_font_extend(self, font_extend): - # Set the Format font_extend property. + """ + Set the font extend property. + + Args: + font_extend: The font extend property. + + Returns: + Nothing. + + """ self.font_extend = font_extend def set_theme(self, theme): - # Set the Format theme property. + """ + Set the theme property. + + Args: + theme: Format theme. + + Returns: + Nothing. + + """ self.theme = theme def set_hyperlink(self, hyperlink=True): - # Set the properties for the hyperlink style. This isn't - # currently public. To be fixed when styles are supported. + """ + Set the properties for the hyperlink style. + + Args: + hyperlink: Default is True, turns property on. + + Returns: + Nothing. + + """ self.xf_id = 1 self.set_underline(1) self.set_theme(10) self.hyperlink = hyperlink def set_color_indexed(self, color_index): - # Used in the cell comment format. + """ + Set the color index property. Some fundamental format properties use an + indexed color instead of a rbg or theme color. + + Args: + color_index: Generally 0 or 1. + + Returns: + Nothing. + + """ self.color_indexed = color_index def set_font_only(self, font_only=True): - # Used in the cell comment format. - self.font_only = font_only + """ + Set property to indicate that the format is used for fonts only. - # Compatibility methods. - def set_font(self, font_name): - # For compatibility with Excel::Writer::XLSX. - self.font_name = font_name + Args: + font_only: Default is True, turns property on. - def set_size(self, font_size): - # For compatibility with Excel::Writer::XLSX. - self.font_size = font_size + Returns: + Nothing. - def set_color(self, font_color): - # For compatibility with Excel::Writer::XLSX. - self.font_color = self._get_color(font_color) + """ + self.font_only = font_only ########################################################################### # @@ -766,6 +930,7 @@ def set_color(self, font_color): ########################################################################### def _get_align_properties(self): + # pylint: disable=too-many-boolean-expressions # Return properties for an Style xf sub-element. changed = 0 align = [] @@ -862,14 +1027,14 @@ def _get_align_properties(self): def _get_protection_properties(self): # Return properties for an Excel XML element. - attribs = [] + attributes = [] if not self.locked: - attribs.append(("locked", 0)) + attributes.append(("locked", 0)) if self.hidden: - attribs.append(("hidden", 1)) + attributes.append(("hidden", 1)) - return attribs + return attributes def _get_format_key(self): # Returns a unique hash key for a format. Used by Workbook. @@ -963,39 +1128,39 @@ def _get_xf_index(self): if self.xf_index is not None: # Format already has an index number so return it. return self.xf_index - else: - # Format doesn't have an index number so assign one. - key = self._get_format_key() - - if key in self.xf_format_indices: - # Format matches existing format with an index. - return self.xf_format_indices[key] - else: - # New format requiring an index. Note. +1 since Excel - # has an implicit "General" format at index 0. - index = 1 + len(self.xf_format_indices) - self.xf_format_indices[key] = index - self.xf_index = index - return index + + # Format doesn't have an index number so assign one. + key = self._get_format_key() + + if key in self.xf_format_indices: + # Format matches existing format with an index. + return self.xf_format_indices[key] + + # New format requiring an index. Note. +1 since Excel + # has an implicit "General" format at index 0. + index = 1 + len(self.xf_format_indices) + self.xf_format_indices[key] = index + self.xf_index = index + return index def _get_dxf_index(self): # Returns the DXF index number used by Excel to identify a format. if self.dxf_index is not None: # Format already has an index number so return it. return self.dxf_index - else: - # Format doesn't have an index number so assign one. - key = self._get_format_key() - - if key in self.dxf_format_indices: - # Format matches existing format with an index. - return self.dxf_format_indices[key] - else: - # New format requiring an index. - index = len(self.dxf_format_indices) - self.dxf_format_indices[key] = index - self.dxf_index = index - return index + + # Format doesn't have an index number so assign one. + key = self._get_format_key() + + if key in self.dxf_format_indices: + # Format matches existing format with an index. + return self.dxf_format_indices[key] + + # New format requiring an index. + index = len(self.dxf_format_indices) + self.dxf_format_indices[key] = index + self.dxf_index = index + return index def _get_color(self, color): # Used in conjunction with the set_xxx_color methods to convert a @@ -1021,10 +1186,7 @@ def _get_color(self, color): "automatic": "Automatic", } - if color in named_colors: - color = named_colors[color] - - return color + return named_colors.get(color, color) ########################################################################### # diff --git a/xlsxwriter/metadata.py b/xlsxwriter/metadata.py index d18e42ef6..9c370e682 100644 --- a/xlsxwriter/metadata.py +++ b/xlsxwriter/metadata.py @@ -28,7 +28,7 @@ def __init__(self): """ - super(Metadata, self).__init__() + super().__init__() self.has_dynamic_functions = False self.has_embedded_images = False self.num_embedded_images = 0 @@ -238,10 +238,10 @@ def _write_cell_metadata(self): def _write_value_metadata(self): # Write the element. count = self.num_embedded_images - type = 1 + rc_type = 1 if self.has_dynamic_functions: - type = 2 + rc_type = 2 attributes = [("count", count)] @@ -250,15 +250,15 @@ def _write_value_metadata(self): # Write the rc elements. for index in range(self.num_embedded_images): self._xml_start_tag("bk") - self._write_rc(type, index) + self._write_rc(rc_type, index) self._xml_end_tag("bk") self._xml_end_tag("valueMetadata") - def _write_rc(self, type, index): + def _write_rc(self, rc_type, index): # Write the element. attributes = [ - ("t", type), + ("t", rc_type), ("v", index), ] diff --git a/xlsxwriter/packager.py b/xlsxwriter/packager.py index 92a97f148..002798414 100644 --- a/xlsxwriter/packager.py +++ b/xlsxwriter/packager.py @@ -35,7 +35,7 @@ from .exceptions import EmptyChartSeries -class Packager(): +class Packager: """ A class for writing the Excel XLSX Packager file. @@ -89,7 +89,7 @@ def __init__(self): """ - super(Packager, self).__init__() + super().__init__() self.tmpdir = "" self.in_memory = False @@ -416,7 +416,7 @@ def _write_custom_file(self): properties = self.workbook.custom_properties custom = Custom() - if not len(properties): + if not properties: return custom._set_properties(properties) @@ -736,6 +736,7 @@ def _write_rich_value_rels_files(self): rels._assemble_xml_file() def _add_image_files(self): + # pylint: disable=consider-using-with # Write the /xl/media/image?.xml files. workbook = self.workbook index = 1 @@ -784,6 +785,7 @@ def _add_image_files(self): index += 1 def _add_vba_project_signature(self): + # pylint: disable=consider-using-with # Copy in a vbaProjectSignature.bin file. vba_project_signature = self.workbook.vba_project_signature vba_project_signature_is_stream = self.workbook.vba_project_signature_is_stream @@ -819,6 +821,7 @@ def _add_vba_project_signature(self): self.filenames.append((os_filename, xml_vba_signature_name, True)) def _add_vba_project(self): + # pylint: disable=consider-using-with # Copy in a vbaProject.bin file. vba_project = self.workbook.vba_project vba_project_is_stream = self.workbook.vba_project_is_stream diff --git a/xlsxwriter/relationships.py b/xlsxwriter/relationships.py index defa51179..4705ad97c 100644 --- a/xlsxwriter/relationships.py +++ b/xlsxwriter/relationships.py @@ -10,9 +10,9 @@ from . import xmlwriter # Long namespace strings used in the class. -schema_root = "http://schemas.openxmlformats.org" -package_schema = schema_root + "/package/2006/relationships" -document_schema = schema_root + "/officeDocument/2006/relationships" +SCHEMA_ROOT = "http://schemas.openxmlformats.org" +PACKAGE_SCHEMA = SCHEMA_ROOT + "/package/2006/relationships" +DOCUMENT_SCHEMA = SCHEMA_ROOT + "/officeDocument/2006/relationships" class Relationships(xmlwriter.XMLwriter): @@ -34,7 +34,7 @@ def __init__(self): """ - super(Relationships, self).__init__() + super().__init__() self.relationships = [] self.id = 1 @@ -58,13 +58,13 @@ def _assemble_xml_file(self): def _add_document_relationship(self, rel_type, target, target_mode=None): # Add container relationship to XLSX .rels xml files. - rel_type = document_schema + rel_type + rel_type = DOCUMENT_SCHEMA + rel_type self.relationships.append((rel_type, target, target_mode)) def _add_package_relationship(self, rel_type, target): # Add container relationship to XLSX .rels xml files. - rel_type = package_schema + rel_type + rel_type = PACKAGE_SCHEMA + rel_type self.relationships.append((rel_type, target, None)) @@ -108,7 +108,7 @@ def _write_relationships(self): attributes = [ ( "xmlns", - package_schema, + PACKAGE_SCHEMA, ) ] diff --git a/xlsxwriter/rich_value.py b/xlsxwriter/rich_value.py index b262dd196..e4ede9198 100644 --- a/xlsxwriter/rich_value.py +++ b/xlsxwriter/rich_value.py @@ -29,7 +29,7 @@ def __init__(self): """ - super(RichValue, self).__init__() + super().__init__() self.embedded_images = [] ########################################################################### diff --git a/xlsxwriter/rich_value_rel.py b/xlsxwriter/rich_value_rel.py index 3a288f308..a39944bc9 100644 --- a/xlsxwriter/rich_value_rel.py +++ b/xlsxwriter/rich_value_rel.py @@ -29,7 +29,7 @@ def __init__(self): """ - super(RichValueRel, self).__init__() + super().__init__() self.num_embedded_images = 0 ########################################################################### diff --git a/xlsxwriter/rich_value_structure.py b/xlsxwriter/rich_value_structure.py index 9e1e1753b..107b39c40 100644 --- a/xlsxwriter/rich_value_structure.py +++ b/xlsxwriter/rich_value_structure.py @@ -29,7 +29,7 @@ def __init__(self): """ - super(RichValueStructure, self).__init__() + super().__init__() self.has_embedded_descriptions = False ########################################################################### @@ -88,11 +88,11 @@ def _write_s(self): self._xml_end_tag("s") - def _write_k(self, name, type): + def _write_k(self, name, k_type): # Write the element. attributes = [ ("n", name), - ("t", type), + ("t", k_type), ] self._xml_empty_tag("k", attributes) diff --git a/xlsxwriter/rich_value_types.py b/xlsxwriter/rich_value_types.py index 584ad350e..cc9522f8b 100644 --- a/xlsxwriter/rich_value_types.py +++ b/xlsxwriter/rich_value_types.py @@ -17,20 +17,6 @@ class RichValueTypes(xmlwriter.XMLwriter): """ - ########################################################################### - # - # Public API. - # - ########################################################################### - - def __init__(self): - """ - Constructor. - - """ - - super(RichValueTypes, self).__init__() - ########################################################################### # # Private API. diff --git a/xlsxwriter/shape.py b/xlsxwriter/shape.py index c3468ce17..79416099e 100644 --- a/xlsxwriter/shape.py +++ b/xlsxwriter/shape.py @@ -9,7 +9,7 @@ from warnings import warn -class Shape(): +class Shape: """ A class for to represent Excel XLSX shape objects. @@ -27,7 +27,7 @@ def __init__(self, shape_type, name, options): Constructor. """ - super(Shape, self).__init__() + super().__init__() self.name = name self.shape_type = shape_type self.connect = 0 @@ -125,7 +125,7 @@ def _get_line_properties(line): line["dash_type"] = dash_types[dash_type] else: warn(f"Unknown dash type '{dash_type}'") - return + return {} line["defined"] = True @@ -150,18 +150,18 @@ def _get_pattern_properties(pattern): # Convert user defined pattern to the structure required internally. if not pattern: - return + return {} # Copy the user defined properties since they will be modified. pattern = copy.deepcopy(pattern) if not pattern.get("pattern"): warn("Pattern must include 'pattern'") - return + return {} if not pattern.get("fg_color"): warn("Pattern must include 'fg_color'") - return + return {} types = { "percent_5": "pct5", @@ -217,9 +217,9 @@ def _get_pattern_properties(pattern): # Check for valid types. if pattern["pattern"] not in types: warn(f"unknown pattern type '{pattern['pattern']}'") - return - else: - pattern["pattern"] = types[pattern["pattern"]] + return {} + + pattern["pattern"] = types[pattern["pattern"]] # Specify a default background color. pattern["bg_color"] = pattern.get("bg_color", "#FFFFFF") @@ -228,10 +228,11 @@ def _get_pattern_properties(pattern): @staticmethod def _get_gradient_properties(gradient): + # pylint: disable=too-many-return-statements # Convert user defined gradient to the structure required internally. if not gradient: - return + return {} # Copy the user defined properties since they will be modified. gradient = copy.deepcopy(gradient) @@ -246,24 +247,24 @@ def _get_gradient_properties(gradient): # Check the colors array exists and is valid. if "colors" not in gradient or not isinstance(gradient["colors"], list): warn("Gradient must include colors list") - return + return {} # Check the colors array has the required number of entries. if not 2 <= len(gradient["colors"]) <= 10: warn("Gradient colors list must at least 2 values and not more than 10") - return + return {} if "positions" in gradient: # Check the positions array has the right number of entries. if len(gradient["positions"]) != len(gradient["colors"]): warn("Gradient positions not equal to number of colors") - return + return {} # Check the positions are in the correct range. for pos in gradient["positions"]: if not 0 <= pos <= 100: warn("Gradient position must be in the range 0 <= position <= 100") - return + return {} else: # Use the default gradient positions. if len(gradient["colors"]) == 2: @@ -277,13 +278,13 @@ def _get_gradient_properties(gradient): else: warn("Must specify gradient positions") - return + return {} angle = gradient.get("angle") if angle: if not 0 <= angle < 360: warn("Gradient angle must be in the range 0 <= angle < 360") - return + return {} else: gradient["angle"] = 90 @@ -295,7 +296,7 @@ def _get_gradient_properties(gradient): gradient["type"] = types[gradient_type] else: warn(f"Unknown gradient type '{gradient_type}") - return + return {} else: gradient["type"] = "linear" diff --git a/xlsxwriter/sharedstrings.py b/xlsxwriter/sharedstrings.py index d628e71aa..82b628410 100644 --- a/xlsxwriter/sharedstrings.py +++ b/xlsxwriter/sharedstrings.py @@ -8,7 +8,7 @@ # Package imports. from . import xmlwriter -from .utility import preserve_whitespace +from .utility import _preserve_whitespace class SharedStrings(xmlwriter.XMLwriter): @@ -29,7 +29,7 @@ def __init__(self): """ - super(SharedStrings, self).__init__() + super().__init__() self.string_table = None @@ -89,7 +89,7 @@ def _write_si(self, string): string = self._escape_control_characters(string) # Add attribute to preserve leading or trailing whitespace. - if preserve_whitespace(string): + if _preserve_whitespace(string): attributes.append(("xml:space", "preserve")) # Write any rich strings without further tags. @@ -100,7 +100,7 @@ def _write_si(self, string): # A metadata class to store Excel strings between worksheets. -class SharedStringTable(): +class SharedStringTable: """ A class to track Excel shared strings between worksheets. @@ -121,11 +121,11 @@ def _get_shared_string_index(self, string): self.count += 1 self.unique_count += 1 return index - else: - # String exists in the table. - index = self.string_table[string] - self.count += 1 - return index + + # String exists in the table. + index = self.string_table[string] + self.count += 1 + return index def _get_shared_string(self, index): """ " Get a shared string from the index.""" diff --git a/xlsxwriter/styles.py b/xlsxwriter/styles.py index f9c17d62a..b60cffa77 100644 --- a/xlsxwriter/styles.py +++ b/xlsxwriter/styles.py @@ -29,7 +29,7 @@ def __init__(self): """ - super(Styles, self).__init__() + super().__init__() self.xf_formats = [] self.palette = [] @@ -188,10 +188,7 @@ def _write_num_fmt(self, num_fmt_id, format_code): # Set the format code for built-in number formats. if num_fmt_id < 164: - if num_fmt_id in format_codes: - format_code = format_codes[num_fmt_id] - else: - format_code = "General" + format_code = format_codes.get(num_fmt_id, "General") attributes = [ ("numFmtId", num_fmt_id), diff --git a/xlsxwriter/table.py b/xlsxwriter/table.py index db4fe4feb..cadba8949 100644 --- a/xlsxwriter/table.py +++ b/xlsxwriter/table.py @@ -28,7 +28,7 @@ def __init__(self): """ - super(Table, self).__init__() + super().__init__() self.properties = {} diff --git a/xlsxwriter/test/comparison/test_cond_format01.py b/xlsxwriter/test/comparison/test_cond_format01.py index 441f668ea..60f2501de 100644 --- a/xlsxwriter/test/comparison/test_cond_format01.py +++ b/xlsxwriter/test/comparison/test_cond_format01.py @@ -28,7 +28,7 @@ def test_create_file(self): cell_format = workbook.add_format( { - "color": "#9C0006", + "font_color": "#9C0006", "bg_color": "#FFC7CE", "font_condense": 1, "font_extend": 1, diff --git a/xlsxwriter/test/comparison/test_cond_format08.py b/xlsxwriter/test/comparison/test_cond_format08.py index ed577cb29..c9716255e 100644 --- a/xlsxwriter/test/comparison/test_cond_format08.py +++ b/xlsxwriter/test/comparison/test_cond_format08.py @@ -28,7 +28,7 @@ def test_create_file(self): format1 = workbook.add_format( { - "color": "#9C6500", + "font_color": "#9C6500", "bg_color": "#FFEB9C", "font_condense": 1, "font_extend": 1, diff --git a/xlsxwriter/test/comparison/test_cond_format09.py b/xlsxwriter/test/comparison/test_cond_format09.py index b0f266c80..9bfd46ed8 100644 --- a/xlsxwriter/test/comparison/test_cond_format09.py +++ b/xlsxwriter/test/comparison/test_cond_format09.py @@ -28,7 +28,7 @@ def test_create_file(self): format = workbook.add_format( { - "color": "#9C6500", + "font_color": "#9C6500", "bg_color": "#FFEB9C", "font_condense": 1, "font_extend": 1, diff --git a/xlsxwriter/test/comparison/test_format13.py b/xlsxwriter/test/comparison/test_format13.py index 4b053fc4f..e2155ccc9 100644 --- a/xlsxwriter/test/comparison/test_format13.py +++ b/xlsxwriter/test/comparison/test_format13.py @@ -30,7 +30,7 @@ def test_create_file(self): font_format = workbook.add_format() - font_format.set_font("B Nazanin") + font_format.set_font_name("B Nazanin") font_format.set_font_family(0) font_format.set_font_charset(178) diff --git a/xlsxwriter/test/comparison/test_format20.py b/xlsxwriter/test/comparison/test_format20.py index 55e42c7a6..d2070c1cf 100644 --- a/xlsxwriter/test/comparison/test_format20.py +++ b/xlsxwriter/test/comparison/test_format20.py @@ -26,7 +26,7 @@ def test_create_file(self): worksheet = workbook.add_worksheet() - format1 = workbook.add_format({"color": "automatic"}) + format1 = workbook.add_format({"font_color": "automatic"}) worksheet.write(0, 0, "Foo", format1) diff --git a/xlsxwriter/test/comparison/test_format21.py b/xlsxwriter/test/comparison/test_format21.py index f5dbb2ee4..9e72e5050 100644 --- a/xlsxwriter/test/comparison/test_format21.py +++ b/xlsxwriter/test/comparison/test_format21.py @@ -28,7 +28,7 @@ def test_create_file(self): format1 = workbook.add_format( { - "color": "automatic", + "font_color": "automatic", "fg_color": "automatic", "bg_color": "red", "pattern": 6, diff --git a/xlsxwriter/test/comparison/test_format22.py b/xlsxwriter/test/comparison/test_format22.py index 873b93a03..5cd94074f 100644 --- a/xlsxwriter/test/comparison/test_format22.py +++ b/xlsxwriter/test/comparison/test_format22.py @@ -28,7 +28,7 @@ def test_create_file(self): format1 = workbook.add_format( { - "color": "automatic", + "font_color": "automatic", "border": 1, "border_color": "automatic", } diff --git a/xlsxwriter/test/comparison/test_format23.py b/xlsxwriter/test/comparison/test_format23.py index 74a321239..8f327768a 100644 --- a/xlsxwriter/test/comparison/test_format23.py +++ b/xlsxwriter/test/comparison/test_format23.py @@ -28,7 +28,7 @@ def test_create_file(self): format1 = workbook.add_format( { - "color": "automatic", + "font_color": "automatic", "diag_border": 1, "diag_type": 2, "diag_color": "automatic", diff --git a/xlsxwriter/test/comparison/test_hyperlink10.py b/xlsxwriter/test/comparison/test_hyperlink10.py index 4842dce72..54856ff3d 100644 --- a/xlsxwriter/test/comparison/test_hyperlink10.py +++ b/xlsxwriter/test/comparison/test_hyperlink10.py @@ -25,9 +25,9 @@ def test_create_file(self): workbook = Workbook(self.got_filename) worksheet = workbook.add_worksheet() - format = workbook.add_format({"color": "red", "underline": 1}) + cell_format = workbook.add_format({"font_color": "red", "underline": 1}) - worksheet.write_url("A1", "http://www.perl.org/", format) + worksheet.write_url("A1", "http://www.perl.org/", cell_format) workbook.close() @@ -39,9 +39,9 @@ def test_create_file_write(self): workbook = Workbook(self.got_filename) worksheet = workbook.add_worksheet() - format = workbook.add_format({"color": "red", "underline": 1}) + cell_format = workbook.add_format({"font_color": "red", "underline": 1}) - worksheet.write("A1", "http://www.perl.org/", format) + worksheet.write("A1", "http://www.perl.org/", cell_format) workbook.close() diff --git a/xlsxwriter/test/comparison/test_hyperlink11.py b/xlsxwriter/test/comparison/test_hyperlink11.py index 91aa953a8..2126cc159 100644 --- a/xlsxwriter/test/comparison/test_hyperlink11.py +++ b/xlsxwriter/test/comparison/test_hyperlink11.py @@ -25,7 +25,7 @@ def test_link_format_explicit(self): workbook = Workbook(self.got_filename) worksheet = workbook.add_worksheet() - url_format = workbook.add_format({"color": "blue", "underline": 1}) + url_format = workbook.add_format({"font_color": "blue", "underline": 1}) worksheet.write_url("A1", "http://www.perl.org/", url_format) diff --git a/xlsxwriter/test/comparison/test_hyperlink12.py b/xlsxwriter/test/comparison/test_hyperlink12.py index 90b976d45..899ebc09c 100644 --- a/xlsxwriter/test/comparison/test_hyperlink12.py +++ b/xlsxwriter/test/comparison/test_hyperlink12.py @@ -25,11 +25,11 @@ def test_create_file(self): workbook = Workbook(self.got_filename) worksheet = workbook.add_worksheet() - format = workbook.add_format({"color": "blue", "underline": 1}) + cell_format = workbook.add_format({"font_color": "blue", "underline": 1}) - worksheet.write_url("A1", "mailto:jmcnamara@cpan.org", format) + worksheet.write_url("A1", "mailto:jmcnamara@cpan.org", cell_format) - worksheet.write_url("A3", "ftp://perl.org/", format) + worksheet.write_url("A3", "ftp://perl.org/", cell_format) workbook.close() @@ -41,11 +41,11 @@ def test_create_file_write(self): workbook = Workbook(self.got_filename) worksheet = workbook.add_worksheet() - format = workbook.add_format({"color": "blue", "underline": 1}) + cell_format = workbook.add_format({"font_color": "blue", "underline": 1}) - worksheet.write("A1", "mailto:jmcnamara@cpan.org", format) + worksheet.write("A1", "mailto:jmcnamara@cpan.org", cell_format) - worksheet.write("A3", "ftp://perl.org/", format) + worksheet.write("A3", "ftp://perl.org/", cell_format) workbook.close() diff --git a/xlsxwriter/test/comparison/test_hyperlink13.py b/xlsxwriter/test/comparison/test_hyperlink13.py index 69adbe816..82ea0efdb 100644 --- a/xlsxwriter/test/comparison/test_hyperlink13.py +++ b/xlsxwriter/test/comparison/test_hyperlink13.py @@ -25,10 +25,10 @@ def test_create_file(self): workbook = Workbook(self.got_filename) worksheet = workbook.add_worksheet() - format = workbook.add_format({"align": "center"}) + cell_format = workbook.add_format({"align": "center"}) - worksheet.merge_range("C4:E5", "", format) - worksheet.write_url("C4", "http://www.perl.org/", format) + worksheet.merge_range("C4:E5", "", cell_format) + worksheet.write_url("C4", "http://www.perl.org/", cell_format) workbook.close() diff --git a/xlsxwriter/test/comparison/test_hyperlink14.py b/xlsxwriter/test/comparison/test_hyperlink14.py index b91434dcf..391ef87b7 100644 --- a/xlsxwriter/test/comparison/test_hyperlink14.py +++ b/xlsxwriter/test/comparison/test_hyperlink14.py @@ -25,14 +25,14 @@ def test_create_file(self): workbook = Workbook(self.got_filename) worksheet = workbook.add_worksheet() - format = workbook.add_format({"align": "center"}) + cell_format = workbook.add_format({"align": "center"}) worksheet.merge_range( "C4:E5", "", - format, + cell_format, ) - worksheet.write_url("C4", "http://www.perl.org/", format, "Perl Home") + worksheet.write_url("C4", "http://www.perl.org/", cell_format, "Perl Home") workbook.close() diff --git a/xlsxwriter/test/comparison/test_hyperlink20.py b/xlsxwriter/test/comparison/test_hyperlink20.py index de7314fb7..053c92dd8 100644 --- a/xlsxwriter/test/comparison/test_hyperlink20.py +++ b/xlsxwriter/test/comparison/test_hyperlink20.py @@ -28,8 +28,8 @@ def test_hyperlink_formatting_explicit(self): workbook.custom_colors = ["FF0000FF"] worksheet = workbook.add_worksheet() - format1 = workbook.add_format({"color": "blue", "underline": 1}) - format2 = workbook.add_format({"color": "red", "underline": 1}) + format1 = workbook.add_format({"font_color": "blue", "underline": 1}) + format2 = workbook.add_format({"font_color": "red", "underline": 1}) worksheet.write_url("A1", "http://www.python.org/1", format1) worksheet.write_url("A2", "http://www.python.org/2", format2) diff --git a/xlsxwriter/test/comparison/test_hyperlink28.py b/xlsxwriter/test/comparison/test_hyperlink28.py index 33422edd4..5aee94d92 100644 --- a/xlsxwriter/test/comparison/test_hyperlink28.py +++ b/xlsxwriter/test/comparison/test_hyperlink28.py @@ -25,9 +25,9 @@ def test_create_file(self): workbook = Workbook(self.got_filename) worksheet = workbook.add_worksheet() - format = workbook.add_format({"hyperlink": True}) + cell_format = workbook.add_format({"hyperlink": True}) - worksheet.write_url("A1", "http://www.perl.org/", format) + worksheet.write_url("A1", "http://www.perl.org/", cell_format) workbook.close() @@ -39,9 +39,9 @@ def test_create_file_with_workbook_format(self): workbook = Workbook(self.got_filename) worksheet = workbook.add_worksheet() - format = workbook.get_default_url_format() + cell_format = workbook.get_default_url_format() - worksheet.write_url("A1", "http://www.perl.org/", format) + worksheet.write_url("A1", "http://www.perl.org/", cell_format) workbook.close() diff --git a/xlsxwriter/test/comparison/test_hyperlink29.py b/xlsxwriter/test/comparison/test_hyperlink29.py index 5b20d9bc3..2e5e89e24 100644 --- a/xlsxwriter/test/comparison/test_hyperlink29.py +++ b/xlsxwriter/test/comparison/test_hyperlink29.py @@ -26,7 +26,7 @@ def test_create_file(self): worksheet = workbook.add_worksheet() format1 = workbook.add_format({"hyperlink": True}) - format2 = workbook.add_format({"color": "red", "underline": 1}) + format2 = workbook.add_format({"font_color": "red", "underline": 1}) worksheet.write_url("A1", "http://www.perl.org/", format1) worksheet.write_url("A2", "http://www.perl.com/", format2) @@ -41,7 +41,7 @@ def test_create_file_with_default_format(self): workbook = Workbook(self.got_filename) worksheet = workbook.add_worksheet() - format2 = workbook.add_format({"color": "red", "underline": 1}) + format2 = workbook.add_format({"font_color": "red", "underline": 1}) worksheet.write_url("A1", "http://www.perl.org/") worksheet.write_url("A2", "http://www.perl.com/", format2) diff --git a/xlsxwriter/test/comparison/test_hyperlink30.py b/xlsxwriter/test/comparison/test_hyperlink30.py index 92455ba10..ca318fed3 100644 --- a/xlsxwriter/test/comparison/test_hyperlink30.py +++ b/xlsxwriter/test/comparison/test_hyperlink30.py @@ -28,8 +28,8 @@ def test_create_file(self): worksheet = workbook.add_worksheet() format1 = workbook.add_format({"hyperlink": 1}) - format2 = workbook.add_format({"color": "red", "underline": 1}) - format3 = workbook.add_format({"color": "blue", "underline": 1}) + format2 = workbook.add_format({"font_color": "red", "underline": 1}) + format3 = workbook.add_format({"font_color": "blue", "underline": 1}) worksheet.write_url("A1", "http://www.python.org/1", format1) worksheet.write_url("A2", "http://www.python.org/2", format2) @@ -47,8 +47,8 @@ def test_create_file_with_default_format(self): workbook.custom_colors = ["FF0000FF"] worksheet = workbook.add_worksheet() - format2 = workbook.add_format({"color": "red", "underline": 1}) - format3 = workbook.add_format({"color": "blue", "underline": 1}) + format2 = workbook.add_format({"font_color": "red", "underline": 1}) + format3 = workbook.add_format({"font_color": "blue", "underline": 1}) worksheet.write_url("A1", "http://www.python.org/1") worksheet.write_url("A2", "http://www.python.org/2", format2) diff --git a/xlsxwriter/test/comparison/test_merge_cells01.py b/xlsxwriter/test/comparison/test_merge_cells01.py index 2ab6aaa0a..f219d83da 100644 --- a/xlsxwriter/test/comparison/test_merge_cells01.py +++ b/xlsxwriter/test/comparison/test_merge_cells01.py @@ -25,14 +25,14 @@ def test_create_file(self): workbook = Workbook(self.got_filename) worksheet = workbook.add_worksheet() - format = workbook.add_format({"align": "center"}) + cell_format = workbook.add_format({"align": "center"}) worksheet.set_selection("A4") - worksheet.merge_range("A1:A2", "col1", format) - worksheet.merge_range("B1:B2", "col2", format) - worksheet.merge_range("C1:C2", "col3", format) - worksheet.merge_range("D1:D2", "col4", format) + worksheet.merge_range("A1:A2", "col1", cell_format) + worksheet.merge_range("B1:B2", "col2", cell_format) + worksheet.merge_range("C1:C2", "col3", cell_format) + worksheet.merge_range("D1:D2", "col4", cell_format) workbook.close() diff --git a/xlsxwriter/test/comparison/test_merge_range01.py b/xlsxwriter/test/comparison/test_merge_range01.py index 2a2ec7811..402dfb0ff 100644 --- a/xlsxwriter/test/comparison/test_merge_range01.py +++ b/xlsxwriter/test/comparison/test_merge_range01.py @@ -26,9 +26,9 @@ def test_create_file(self): worksheet = workbook.add_worksheet() - format = workbook.add_format({"align": "center"}) + cell_format = workbook.add_format({"align": "center"}) - worksheet.merge_range(1, 1, 1, 3, "Foo", format) + worksheet.merge_range(1, 1, 1, 3, "Foo", cell_format) workbook.close() diff --git a/xlsxwriter/test/comparison/test_merge_range02.py b/xlsxwriter/test/comparison/test_merge_range02.py index d64557339..dda27be25 100644 --- a/xlsxwriter/test/comparison/test_merge_range02.py +++ b/xlsxwriter/test/comparison/test_merge_range02.py @@ -26,9 +26,9 @@ def test_create_file(self): worksheet = workbook.add_worksheet() - format = workbook.add_format({"align": "center"}) + cell_format = workbook.add_format({"align": "center"}) - worksheet.merge_range(1, 1, 5, 3, "Foo", format) + worksheet.merge_range(1, 1, 5, 3, "Foo", cell_format) workbook.close() diff --git a/xlsxwriter/test/comparison/test_merge_range03.py b/xlsxwriter/test/comparison/test_merge_range03.py index ae655fc0e..d2565d8f3 100644 --- a/xlsxwriter/test/comparison/test_merge_range03.py +++ b/xlsxwriter/test/comparison/test_merge_range03.py @@ -26,11 +26,11 @@ def test_create_file(self): worksheet = workbook.add_worksheet() - format = workbook.add_format({"align": "center"}) + cell_format = workbook.add_format({"align": "center"}) - worksheet.merge_range(1, 1, 1, 2, "Foo", format) - worksheet.merge_range(1, 3, 1, 4, "Foo", format) - worksheet.merge_range(1, 5, 1, 6, "Foo", format) + worksheet.merge_range(1, 1, 1, 2, "Foo", cell_format) + worksheet.merge_range(1, 3, 1, 4, "Foo", cell_format) + worksheet.merge_range(1, 5, 1, 6, "Foo", cell_format) workbook.close() diff --git a/xlsxwriter/test/comparison/test_merge_range04.py b/xlsxwriter/test/comparison/test_merge_range04.py index e8d8246dd..cdb06f2e7 100644 --- a/xlsxwriter/test/comparison/test_merge_range04.py +++ b/xlsxwriter/test/comparison/test_merge_range04.py @@ -26,9 +26,9 @@ def test_create_file(self): worksheet = workbook.add_worksheet() - format = workbook.add_format({"align": "center", "bold": 1}) + cell_format = workbook.add_format({"align": "center", "bold": 1}) - worksheet.merge_range(1, 1, 1, 3, "Foo", format) + worksheet.merge_range(1, 1, 1, 3, "Foo", cell_format) workbook.close() diff --git a/xlsxwriter/test/comparison/test_merge_range05.py b/xlsxwriter/test/comparison/test_merge_range05.py index e205c1795..427dd87b4 100644 --- a/xlsxwriter/test/comparison/test_merge_range05.py +++ b/xlsxwriter/test/comparison/test_merge_range05.py @@ -26,9 +26,9 @@ def test_create_file(self): worksheet = workbook.add_worksheet() - format = workbook.add_format({"align": "center"}) + cell_format = workbook.add_format({"align": "center"}) - worksheet.merge_range(1, 1, 1, 3, 123, format) + worksheet.merge_range(1, 1, 1, 3, 123, cell_format) workbook.close() diff --git a/xlsxwriter/test/comparison/test_optimize03.py b/xlsxwriter/test/comparison/test_optimize03.py index 4b915392b..cea5999ac 100644 --- a/xlsxwriter/test/comparison/test_optimize03.py +++ b/xlsxwriter/test/comparison/test_optimize03.py @@ -61,7 +61,7 @@ def test_create_file(self): "italic": 1, "font_color": "red", "font_size": 18, - "font": "Lucida Calligraphy", + "font_name": "Lucida Calligraphy", } ) diff --git a/xlsxwriter/test/comparison/test_rich_string06.py b/xlsxwriter/test/comparison/test_rich_string06.py index 2f18ba937..d03c03bed 100644 --- a/xlsxwriter/test/comparison/test_rich_string06.py +++ b/xlsxwriter/test/comparison/test_rich_string06.py @@ -26,7 +26,7 @@ def test_create_file(self): worksheet = workbook.add_worksheet() - red = workbook.add_format({"color": "red"}) + red = workbook.add_format({"font_color": "red"}) worksheet.write("A1", "Foo", red) worksheet.write("A2", "Bar") diff --git a/xlsxwriter/test/comparison/test_rich_string08.py b/xlsxwriter/test/comparison/test_rich_string08.py index 68e034165..69ec83e4d 100644 --- a/xlsxwriter/test/comparison/test_rich_string08.py +++ b/xlsxwriter/test/comparison/test_rich_string08.py @@ -28,11 +28,11 @@ def test_create_file(self): bold = workbook.add_format({"bold": 1}) italic = workbook.add_format({"italic": 1}) - format = workbook.add_format({"align": "center"}) + cell_format = workbook.add_format({"align": "center"}) worksheet.write("A1", "Foo", bold) worksheet.write("A2", "Bar", italic) - worksheet.write_rich_string("A3", "ab", bold, "cd", "efg", format) + worksheet.write_rich_string("A3", "ab", bold, "cd", "efg", cell_format) workbook.close() diff --git a/xlsxwriter/test/comparison/test_types10.py b/xlsxwriter/test/comparison/test_types10.py index 362a5ba88..140a7fe3f 100644 --- a/xlsxwriter/test/comparison/test_types10.py +++ b/xlsxwriter/test/comparison/test_types10.py @@ -11,8 +11,8 @@ import uuid -def write_uuid(worksheet, row, col, token, format=None): - return worksheet.write_string(row, col, str(token), format) +def write_uuid(worksheet, row, col, token, cell_format=None): + return worksheet.write_string(row, col, str(token), cell_format) class TestCompareXLSXFiles(ExcelComparisonTest): diff --git a/xlsxwriter/test/styles/test_write_font.py b/xlsxwriter/test/styles/test_write_font.py index df12a84ca..90408d11c 100644 --- a/xlsxwriter/test/styles/test_write_font.py +++ b/xlsxwriter/test/styles/test_write_font.py @@ -129,7 +129,7 @@ def test_write_font_8(self): def test_write_font_9(self): """Test the _write_font() method. Font size.""" - properties = {"size": 12} + properties = {"font_size": 12} xf_format = Format(properties) @@ -168,7 +168,7 @@ def test_write_font_11(self): def test_write_font_12(self): """Test the _write_font() method. Colour = red.""" - properties = {"color": "#FF0000"} + properties = {"font_color": "#FF0000"} xf_format = Format(properties) @@ -183,13 +183,13 @@ def test_write_font_13(self): """Test the _write_font() method. All font attributes to check order.""" properties = { "bold": 1, - "color": "#FF0000", + "font_color": "#FF0000", "font_outline": 1, "font_script": 1, "font_shadow": 1, "font_strikeout": 1, "italic": 1, - "size": 12, + "font_size": 12, "underline": 1, } diff --git a/xlsxwriter/theme.py b/xlsxwriter/theme.py index 30d8a4bf6..782583665 100644 --- a/xlsxwriter/theme.py +++ b/xlsxwriter/theme.py @@ -9,7 +9,7 @@ from io import StringIO -class Theme(): +class Theme: """ A class for writing the Excel XLSX Theme file. @@ -27,7 +27,7 @@ def __init__(self): Constructor. """ - super(Theme, self).__init__() + super().__init__() self.fh = None self.internal_fh = False @@ -50,6 +50,7 @@ def _set_xml_writer(self, filename): self.fh = filename else: self.internal_fh = True + # pylint: disable=consider-using-with self.fh = open(filename, mode="w", encoding="utf-8") ########################################################################### @@ -61,6 +62,7 @@ def _set_xml_writer(self, filename): def _write_theme_file(self): # Write a default theme.xml file. + # pylint: disable=line-too-long default_theme = """\n""" # noqa self.fh.write(default_theme) diff --git a/xlsxwriter/utility.py b/xlsxwriter/utility.py index 9b7d72bc0..9ff4645f4 100644 --- a/xlsxwriter/utility.py +++ b/xlsxwriter/utility.py @@ -122,17 +122,18 @@ # # https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3AEmoji%3DYes%3A%5D&abb=on&esc=on&g=&i= # -emojis = "\u00A9\u00AE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26A7\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299\U0001F004\U0001F0CF\U0001F170\U0001F171\U0001F17E\U0001F17F\U0001F18E\U0001F191-\U0001F19A\U0001F1E6-\U0001F1FF\U0001F201\U0001F202\U0001F21A\U0001F22F\U0001F232-\U0001F23A\U0001F250\U0001F251\U0001F300-\U0001F321\U0001F324-\U0001F393\U0001F396\U0001F397\U0001F399-\U0001F39B\U0001F39E-\U0001F3F0\U0001F3F3-\U0001F3F5\U0001F3F7-\U0001F4FD\U0001F4FF-\U0001F53D\U0001F549-\U0001F54E\U0001F550-\U0001F567\U0001F56F\U0001F570\U0001F573-\U0001F57A\U0001F587\U0001F58A-\U0001F58D\U0001F590\U0001F595\U0001F596\U0001F5A4\U0001F5A5\U0001F5A8\U0001F5B1\U0001F5B2\U0001F5BC\U0001F5C2-\U0001F5C4\U0001F5D1-\U0001F5D3\U0001F5DC-\U0001F5DE\U0001F5E1\U0001F5E3\U0001F5E8\U0001F5EF\U0001F5F3\U0001F5FA-\U0001F64F\U0001F680-\U0001F6C5\U0001F6CB-\U0001F6D2\U0001F6D5-\U0001F6D7\U0001F6DC-\U0001F6E5\U0001F6E9\U0001F6EB\U0001F6EC\U0001F6F0\U0001F6F3-\U0001F6FC\U0001F7E0-\U0001F7EB\U0001F7F0\U0001F90C-\U0001F93A\U0001F93C-\U0001F945\U0001F947-\U0001F9FF\U0001FA70-\U0001FA7C\U0001FA80-\U0001FA88\U0001FA90-\U0001FABD\U0001FABF-\U0001FAC5\U0001FACE-\U0001FADB\U0001FAE0-\U0001FAE8\U0001FAF0-\U0001FAF8" # noqa +# pylint: disable-next=line-too-long +EMOJIS = "\u00A9\u00AE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26A7\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299\U0001F004\U0001F0CF\U0001F170\U0001F171\U0001F17E\U0001F17F\U0001F18E\U0001F191-\U0001F19A\U0001F1E6-\U0001F1FF\U0001F201\U0001F202\U0001F21A\U0001F22F\U0001F232-\U0001F23A\U0001F250\U0001F251\U0001F300-\U0001F321\U0001F324-\U0001F393\U0001F396\U0001F397\U0001F399-\U0001F39B\U0001F39E-\U0001F3F0\U0001F3F3-\U0001F3F5\U0001F3F7-\U0001F4FD\U0001F4FF-\U0001F53D\U0001F549-\U0001F54E\U0001F550-\U0001F567\U0001F56F\U0001F570\U0001F573-\U0001F57A\U0001F587\U0001F58A-\U0001F58D\U0001F590\U0001F595\U0001F596\U0001F5A4\U0001F5A5\U0001F5A8\U0001F5B1\U0001F5B2\U0001F5BC\U0001F5C2-\U0001F5C4\U0001F5D1-\U0001F5D3\U0001F5DC-\U0001F5DE\U0001F5E1\U0001F5E3\U0001F5E8\U0001F5EF\U0001F5F3\U0001F5FA-\U0001F64F\U0001F680-\U0001F6C5\U0001F6CB-\U0001F6D2\U0001F6D5-\U0001F6D7\U0001F6DC-\U0001F6E5\U0001F6E9\U0001F6EB\U0001F6EC\U0001F6F0\U0001F6F3-\U0001F6FC\U0001F7E0-\U0001F7EB\U0001F7F0\U0001F90C-\U0001F93A\U0001F93C-\U0001F945\U0001F947-\U0001F9FF\U0001FA70-\U0001FA7C\U0001FA80-\U0001FA88\U0001FA90-\U0001FABD\U0001FABF-\U0001FAC5\U0001FACE-\U0001FADB\U0001FAE0-\U0001FAE8\U0001FAF0-\U0001FAF8" # noqa # Compile performance critical regular expressions. -re_leading = re.compile(r"^\s") -re_trailing = re.compile(r"\s$") -re_range_parts = re.compile(r"(\$?)([A-Z]{1,3})(\$?)(\d+)") -re_quote_rule1 = re.compile(rf"[^\w\.{emojis}]") -re_quote_rule2 = re.compile(rf"^[\d\.{emojis}]") -re_quote_rule3 = re.compile(r"^([A-Z]{1,3}\d+)$") -re_quote_rule4_row = re.compile(r"^R(\d+)") -re_quote_rule4_column = re.compile(r"^R?C(\d+)") +RE_LEADING_WHITESPACE = re.compile(r"^\s") +RE_TRAILING_WHITESPACE = re.compile(r"\s$") +RE_RANGE_PARTS = re.compile(r"(\$?)([A-Z]{1,3})(\$?)(\d+)") +RE_QUOTE_RULE1 = re.compile(rf"[^\w\.{EMOJIS}]") +RE_QUOTE_RULE2 = re.compile(rf"^[\d\.{EMOJIS}]") +RE_QUOTE_RULE3 = re.compile(r"^([A-Z]{1,3}\d+)$") +RE_QUOTE_RULE4_ROW = re.compile(r"^R(\d+)") +RE_QUOTE_RULE4_COLUMN = re.compile(r"^R?C(\d+)") def xl_rowcol_to_cell(row, col, row_abs=False, col_abs=False): @@ -240,7 +241,7 @@ def xl_cell_to_rowcol(cell_str): if not cell_str: return 0, 0 - match = re_range_parts.match(cell_str) + match = RE_RANGE_PARTS.match(cell_str) col_str = match.group(2) row_str = match.group(4) @@ -273,23 +274,13 @@ def xl_cell_to_rowcol_abs(cell_str): if not cell_str: return 0, 0, False, False - match = re_range_parts.match(cell_str) + match = RE_RANGE_PARTS.match(cell_str) - col_abs = match.group(1) + col_abs = bool(match.group(1)) col_str = match.group(2) - row_abs = match.group(3) + row_abs = bool(match.group(3)) row_str = match.group(4) - if col_abs: - col_abs = True - else: - col_abs = False - - if row_abs: - row_abs = True - else: - row_abs = False - # Convert base26 column string to number. expn = 0 col = 0 @@ -327,8 +318,8 @@ def xl_range(first_row, first_col, last_row, last_col): if range1 == range2: return range1 - else: - return range1 + ":" + range2 + + return range1 + ":" + range2 def xl_range_abs(first_row, first_col, last_row, last_col): @@ -355,8 +346,8 @@ def xl_range_abs(first_row, first_col, last_row, last_col): if range1 == range2: return range1 - else: - return range1 + ":" + range2 + + return range1 + ":" + range2 def xl_range_formula(sheetname, first_row, first_col, last_row, last_col): @@ -406,13 +397,13 @@ def quote_sheetname(sheetname): # Rule 1. Sheet names that contain anything other than \w and "." # characters must be quoted. # -------------------------------------------------------------------- - if re_quote_rule1.search(sheetname): + if RE_QUOTE_RULE1.search(sheetname): requires_quoting = True # -------------------------------------------------------------------- # Rule 2. Sheet names that start with a digit or "." must be quoted. # -------------------------------------------------------------------- - elif re_quote_rule2.search(sheetname): + elif RE_QUOTE_RULE2.search(sheetname): requires_quoting = True # -------------------------------------------------------------------- @@ -420,12 +411,12 @@ def quote_sheetname(sheetname): # Valid means that the row and column range values must also be within # Excel row and column limits. # -------------------------------------------------------------------- - elif re_quote_rule3.match(uppercase_sheetname): - match = re_quote_rule3.match(uppercase_sheetname) + elif RE_QUOTE_RULE3.match(uppercase_sheetname): + match = RE_QUOTE_RULE3.match(uppercase_sheetname) cell = match.group(1) (row, col) = xl_cell_to_rowcol(cell) - if row >= 0 and row < row_max and col >= 0 and col < col_max: + if 0 <= row < row_max and 0 <= col < col_max: requires_quoting = True # -------------------------------------------------------------------- @@ -440,19 +431,19 @@ def quote_sheetname(sheetname): # -------------------------------------------------------------------- # Rule 4a. Check for sheet names that start with R1 style references. - elif re_quote_rule4_row.match(uppercase_sheetname): - match = re_quote_rule4_row.match(uppercase_sheetname) + elif RE_QUOTE_RULE4_ROW.match(uppercase_sheetname): + match = RE_QUOTE_RULE4_ROW.match(uppercase_sheetname) row = int(match.group(1)) - if row > 0 and row <= row_max: + if 0 < row <= row_max: requires_quoting = True # Rule 4b. Check for sheet names that start with C1 or RC1 style - elif re_quote_rule4_column.match(uppercase_sheetname): - match = re_quote_rule4_column.match(uppercase_sheetname) + elif RE_QUOTE_RULE4_COLUMN.match(uppercase_sheetname): + match = RE_QUOTE_RULE4_COLUMN.match(uppercase_sheetname) col = int(match.group(1)) - if col > 0 and col <= col_max: + if 0 < col <= col_max: requires_quoting = True # Rule 4c. Check for some single R/C references. @@ -490,7 +481,7 @@ def xl_pixel_width(string): return length -def xl_color(color): +def _xl_color(color): # Used in conjunction with the XlsxWriter *color() methods to convert # a color name into an RGB formatted string. These colors are for # backward compatibility with older versions of Excel. @@ -513,8 +504,7 @@ def xl_color(color): "yellow": "#FFFF00", } - if color in named_colors: - color = named_colors[color] + color = named_colors.get(color, color) if not re.match("#[0-9a-fA-F]{6}", color): warn(f"Color '{color}' isn't a valid Excel color") @@ -523,9 +513,9 @@ def xl_color(color): return "FF" + color.lstrip("#").upper() -def get_rgb_color(color): +def _get_rgb_color(color): # Convert the user specified color to an RGB color. - rgb_color = xl_color(color) + rgb_color = _xl_color(color) # Remove leading FF from RGB color for charts. rgb_color = re.sub(r"^FF", "", rgb_color) @@ -533,7 +523,7 @@ def get_rgb_color(color): return rgb_color -def get_sparkline_style(style_id): +def _get_sparkline_style(style_id): styles = [ { "series": {"theme": "4", "tint": "-0.499984740745262"}, @@ -873,14 +863,14 @@ def get_sparkline_style(style_id): return styles[style_id] -def supported_datetime(dt): +def _supported_datetime(dt): # Determine is an argument is a supported datetime object. return isinstance( dt, (datetime.datetime, datetime.date, datetime.time, datetime.timedelta) ) -def remove_datetime_timezone(dt_obj, remove_timezone): +def _remove_datetime_timezone(dt_obj, remove_timezone): # Excel doesn't support timezones in datetimes/times so we remove the # tzinfo from the object if the user has specified that option in the # constructor. @@ -897,7 +887,7 @@ def remove_datetime_timezone(dt_obj, remove_timezone): return dt_obj -def datetime_to_excel_datetime(dt_obj, date_1904, remove_timezone): +def _datetime_to_excel_datetime(dt_obj, date_1904, remove_timezone): # Convert a datetime object to an Excel serial date and time. The integer # part of the number stores the number of days since the epoch and the # fractional part stores the percentage of the day. @@ -914,14 +904,14 @@ def datetime_to_excel_datetime(dt_obj, date_1904, remove_timezone): # We handle datetime .datetime, .date and .time objects but convert # them to datetime.datetime objects and process them in the same way. if isinstance(dt_obj, datetime.datetime): - dt_obj = remove_datetime_timezone(dt_obj, remove_timezone) + dt_obj = _remove_datetime_timezone(dt_obj, remove_timezone) delta = dt_obj - epoch elif isinstance(dt_obj, datetime.date): dt_obj = datetime.datetime.fromordinal(dt_obj.toordinal()) delta = dt_obj - epoch elif isinstance(dt_obj, datetime.time): dt_obj = datetime.datetime.combine(epoch, dt_obj) - dt_obj = remove_datetime_timezone(dt_obj, remove_timezone) + dt_obj = _remove_datetime_timezone(dt_obj, remove_timezone) delta = dt_obj - epoch elif isinstance(dt_obj, datetime.timedelta): is_timedelta = True @@ -951,16 +941,13 @@ def datetime_to_excel_datetime(dt_obj, date_1904, remove_timezone): return excel_time -def preserve_whitespace(string): +def _preserve_whitespace(string): # Check if a string has leading or trailing whitespace that requires a # "preserve" attribute. - if re_leading.search(string) or re_trailing.search(string): - return True - else: - return False + return RE_LEADING_WHITESPACE.search(string) or RE_TRAILING_WHITESPACE.search(string) -def get_image_properties(filename, image_data): +def _get_image_properties(filename, image_data): # Extract dimension information from the image file. height = 0 width = 0 @@ -969,8 +956,8 @@ def get_image_properties(filename, image_data): if not image_data: # Open the image file and read in the data. - fh = open(filename, "rb") - data = fh.read() + with open(filename, "rb") as fh: + data = fh.read() else: # Read the image data from the user supplied byte stream. data = image_data.getvalue() diff --git a/xlsxwriter/vml.py b/xlsxwriter/vml.py index 6fd3f746f..62c8b480a 100644 --- a/xlsxwriter/vml.py +++ b/xlsxwriter/vml.py @@ -17,20 +17,6 @@ class Vml(xmlwriter.XMLwriter): """ - ########################################################################### - # - # Public API. - # - ########################################################################### - - def __init__(self): - """ - Constructor. - - """ - - super(Vml, self).__init__() - ########################################################################### # # Private API. diff --git a/xlsxwriter/workbook.py b/xlsxwriter/workbook.py index 93ac43cbd..054fb6d0a 100644 --- a/xlsxwriter/workbook.py +++ b/xlsxwriter/workbook.py @@ -39,7 +39,7 @@ from .exceptions import DuplicateWorksheetName from .exceptions import FileCreateError from .exceptions import FileSizeError -from .utility import get_image_properties +from .utility import _get_image_properties class Workbook(xmlwriter.XMLwriter): @@ -65,7 +65,7 @@ def __init__(self, filename=None, options=None): if options is None: options = {} - super(Workbook, self).__init__() + super().__init__() self.filename = filename @@ -172,6 +172,7 @@ def __enter__(self): return self def __exit__(self, type, value, traceback): + # pylint: disable=redefined-builtin """Close workbook when exiting "with" statement.""" self.close() @@ -251,7 +252,7 @@ def add_chart(self, options): chart_type = options.get("type") if chart_type is None: warn("Chart type must be defined in add_chart()") - return + return None if chart_type == "area": chart = ChartArea(options) @@ -260,20 +261,20 @@ def add_chart(self, options): elif chart_type == "column": chart = ChartColumn(options) elif chart_type == "doughnut": - chart = ChartDoughnut(options) + chart = ChartDoughnut() elif chart_type == "line": chart = ChartLine(options) elif chart_type == "pie": - chart = ChartPie(options) + chart = ChartPie() elif chart_type == "radar": chart = ChartRadar(options) elif chart_type == "scatter": chart = ChartScatter(options) elif chart_type == "stock": - chart = ChartStock(options) + chart = ChartStock() else: warn(f"Unknown chart type '{chart_type}' in add_chart()") - return + return None # Set the embedded chart name if present. if "name" in options: @@ -296,7 +297,7 @@ def add_vba_project(self, vba_project, is_stream=False): is_stream: vba_project is an in memory byte stream. Returns: - Nothing. + 0 on success. """ if not is_stream and not os.path.exists(vba_project): @@ -309,6 +310,8 @@ def add_vba_project(self, vba_project, is_stream=False): self.vba_project = vba_project self.vba_project_is_stream = is_stream + return 0 + def add_signed_vba_project( self, vba_project, signature, project_is_stream=False, signature_is_stream=False ): @@ -323,7 +326,7 @@ def add_signed_vba_project( signature_is_stream: signature is an in memory byte stream. Returns: - Nothing. + 0 on success. """ if self.add_vba_project(vba_project, project_is_stream) == -1: @@ -336,6 +339,8 @@ def add_signed_vba_project( self.vba_project_signature = signature self.vba_project_signature_is_stream = signature_is_stream + return 0 + def close(self): """ Call finalization code and close file. @@ -347,6 +352,7 @@ def close(self): Nothing. """ + # pylint: disable=raise-missing-from if not self.fileclosed: try: self._store_workbook() @@ -433,7 +439,7 @@ def set_custom_property(self, name, value, property_type=None): property_type: The type of the custom property. Optional. Returns: - Nothing. + 0 on success. """ if name is None or value is None: @@ -473,6 +479,8 @@ def set_custom_property(self, name, value, property_type=None): self.custom_properties.append((name, value, property_type)) + return 0 + def set_calc_mode(self, mode, calc_id=None): """ Set the Excel calculation mode for the workbook. @@ -509,7 +517,7 @@ def define_name(self, name, formula): formula: The cell or range that the defined name refers to. Returns: - Nothing. + 0 on success. """ sheet_index = None @@ -555,6 +563,8 @@ def define_name(self, name, formula): self.defined_names.append([name, sheet_index, formula, False]) + return 0 + def worksheets(self): """ Return a list of the worksheet objects in the workbook. @@ -685,6 +695,7 @@ def _assemble_xml_file(self): self._xml_close() def _store_workbook(self): + # pylint: disable=consider-using-with # Create the xlsx/zip file. try: xlsx_file = ZipFile( @@ -984,11 +995,13 @@ def _prepare_num_formats(self): xf_format.num_format_index = num_format continue - elif num_format == "0": + + if num_format == "0": # Number format '0' is indexed as 1 in Excel. xf_format.num_format_index = 1 continue - elif num_format == "General": + + if num_format == "General": # The 'General' format has an number format index of 0. xf_format.num_format_index = 0 continue @@ -1235,7 +1248,7 @@ def _prepare_drawings(self): _, _, digest, - ) = get_image_properties(filename, image_data) + ) = _get_image_properties(filename, image_data) self.image_types[image_type] = True @@ -1261,7 +1274,7 @@ def _prepare_drawings(self): x_dpi, y_dpi, digest, - ) = get_image_properties(filename, image_data) + ) = _get_image_properties(filename, image_data) self.image_types[image_type] = True @@ -1309,7 +1322,7 @@ def _prepare_drawings(self): x_dpi, y_dpi, digest, - ) = get_image_properties(filename, image_data) + ) = _get_image_properties(filename, image_data) self.image_types[image_type] = True @@ -1347,7 +1360,7 @@ def _prepare_drawings(self): x_dpi, y_dpi, digest, - ) = get_image_properties(filename, image_data) + ) = _get_image_properties(filename, image_data) self.image_types[image_type] = True @@ -1421,8 +1434,8 @@ def _get_sheet_index(self, sheetname): if sheetname in self.sheetnames: return self.sheetnames[sheetname].index - else: - return None + + return None def _prepare_vml(self): # Iterate through the worksheets and set up the VML objects. @@ -1769,7 +1782,7 @@ def _write_defined_name(self, defined_name): # A metadata class to share data between worksheets. -class WorksheetMeta(): +class WorksheetMeta: """ A class to track worksheets data such as the active sheet and the first sheet. @@ -1782,7 +1795,7 @@ def __init__(self): # A helper class to share embedded images between worksheets. -class EmbeddedImages(): +class EmbeddedImages: """ A class to track duplicate embedded images between worksheets. @@ -1793,6 +1806,17 @@ def __init__(self): self.image_indexes = {} def get_image_index(self, image, digest): + """ + Get the index of an embedded image. + + Args: + image: The image to lookup. + digest: The digest of the image. + + Returns: + The image index. + + """ image_index = self.image_indexes.get(digest) if image_index is None: @@ -1803,4 +1827,14 @@ def get_image_index(self, image, digest): return image_index def has_images(self): + """ + Check if the worksheet has embedded images. + + Args: + None. + + Returns: + Boolean. + + """ return len(self.images) > 0 diff --git a/xlsxwriter/worksheet.py b/xlsxwriter/worksheet.py index 9306991e7..c3e5f8fc3 100644 --- a/xlsxwriter/worksheet.py +++ b/xlsxwriter/worksheet.py @@ -6,6 +6,8 @@ # Copyright 2013-2024, John McNamara, jmcnamara@cpan.org # +# pylint: disable=too-many-return-statements + # Standard packages. import datetime import math @@ -34,13 +36,13 @@ from .utility import xl_cell_to_rowcol from .utility import xl_col_to_name from .utility import xl_range -from .utility import xl_color +from .utility import _xl_color from .utility import xl_pixel_width -from .utility import get_sparkline_style -from .utility import supported_datetime -from .utility import datetime_to_excel_datetime -from .utility import get_image_properties -from .utility import preserve_whitespace +from .utility import _get_sparkline_style +from .utility import _supported_datetime +from .utility import _datetime_to_excel_datetime +from .utility import _get_image_properties +from .utility import _preserve_whitespace from .utility import quote_sheetname from .exceptions import DuplicateTableName from .exceptions import OverlappingRange @@ -173,17 +175,17 @@ def column_wrapper(self, *args, **kwargs): # Named tuples used for cell types. # ############################################################################### -cell_string_tuple = namedtuple("String", "string, format") -cell_number_tuple = namedtuple("Number", "number, format") -cell_blank_tuple = namedtuple("Blank", "format") -cell_boolean_tuple = namedtuple("Boolean", "boolean, format") -cell_formula_tuple = namedtuple("Formula", "formula, format, value") -cell_datetime_tuple = namedtuple("Datetime", "number, format") -cell_arformula_tuple = namedtuple( +CellBlankTuple = namedtuple("Blank", "format") +CellErrorTuple = namedtuple("Error", "error, format, value") +CellNumberTuple = namedtuple("Number", "number, format") +CellStringTuple = namedtuple("String", "string, format") +CellBooleanTuple = namedtuple("Boolean", "boolean, format") +CellFormulaTuple = namedtuple("Formula", "formula, format, value") +CellDatetimeTuple = namedtuple("Datetime", "number, format") +CellRichStringTuple = namedtuple("RichString", "string, format, raw_string") +CellArrayFormulaTuple = namedtuple( "ArrayFormula", "formula, format, value, range, atype" ) -cell_rich_string_tuple = namedtuple("RichString", "string, format, raw_string") -cell_error_tuple = namedtuple("Error", "error, format, value") ############################################################################### @@ -209,7 +211,7 @@ def __init__(self): """ - super(Worksheet, self).__init__() + super().__init__() self.name = None self.index = None @@ -338,7 +340,7 @@ def __init__(self): self.autofilter_area = "" self.autofilter_ref = None - self.filter_range = [] + self.filter_range = [0, 9] self.filter_on = 0 self.filter_cols = {} self.filter_type = {} @@ -419,6 +421,7 @@ def __init__(self): self.has_dynamic_arrays = False self.use_future_functions = False self.ignore_write_string = False + self.embedded_images = None # Utility function for writing different types of strings. def _write_token_as_string(self, token, row, col, *args): @@ -454,9 +457,8 @@ def _write_token_as_string(self, token, row, col, *args): return self._write_string(row, col, *args) - else: - # We have a plain string. - return self._write_string(row, col, *args) + # We have a plain string. + return self._write_string(row, col, *args) @convert_cell_args def write(self, row, col, *args): @@ -479,6 +481,7 @@ def write(self, row, col, *args): # Undecorated version of write(). def _write(self, row, col, *args): + # pylint: disable=raise-missing-from # Check the number of args passed. if not args: raise TypeError("write() takes at least 4 arguments (3 given)") @@ -539,7 +542,7 @@ def _write(self, row, col, *args): return self._write_boolean(row, col, *args) # Write datetime objects. - if supported_datetime(token): + if _supported_datetime(token): return self._write_datetime(row, col, *args) # We haven't matched a supported type. Try float. @@ -601,7 +604,7 @@ def _write_string(self, row, col, string, cell_format=None): self._write_single_row(row) # Store the cell data in the worksheet data table. - self.table[row][col] = cell_string_tuple(string_index, cell_format) + self.table[row][col] = CellStringTuple(string_index, cell_format) return str_error @@ -629,9 +632,11 @@ def _write_number(self, row, col, number, cell_format=None): if self.nan_inf_to_errors: if isnan(number): return self._write_formula(row, col, "#NUM!", cell_format, "#NUM!") - elif number == math.inf: + + if number == math.inf: return self._write_formula(row, col, "1/0", cell_format, "#DIV/0!") - elif number == -math.inf: + + if number == -math.inf: return self._write_formula(row, col, "-1/0", cell_format, "#DIV/0!") else: raise TypeError( @@ -651,7 +656,7 @@ def _write_number(self, row, col, number, cell_format=None): self._write_single_row(row) # Store the cell data in the worksheet data table. - self.table[row][col] = cell_number_tuple(number, cell_format) + self.table[row][col] = CellNumberTuple(number, cell_format) return 0 @@ -675,7 +680,7 @@ def write_blank(self, row, col, blank, cell_format=None): return self._write_blank(row, col, blank, cell_format) # Undecorated version of write_blank(). - def _write_blank(self, row, col, blank, cell_format=None): + def _write_blank(self, row, col, _, cell_format=None): # Don't write a blank cell unless it has a format. if cell_format is None: return 0 @@ -689,7 +694,7 @@ def _write_blank(self, row, col, blank, cell_format=None): self._write_single_row(row) # Store the cell data in the worksheet data table. - self.table[row][col] = cell_blank_tuple(cell_format) + self.table[row][col] = CellBlankTuple(cell_format) return 0 @@ -743,7 +748,7 @@ def _write_formula(self, row, col, formula, cell_format=None, value=0): self._write_single_row(row) # Store the cell data in the worksheet data table. - self.table[row][col] = cell_formula_tuple(formula, cell_format, value) + self.table[row][col] = CellFormulaTuple(formula, cell_format, value) return 0 @@ -1092,7 +1097,7 @@ def _write_array_formula( self._write_single_row(first_row) # Store the cell data in the worksheet data table. - self.table[first_row][first_col] = cell_arformula_tuple( + self.table[first_row][first_col] = CellArrayFormulaTuple( formula, cell_format, value, cell_range, atype ) @@ -1141,7 +1146,7 @@ def _write_datetime(self, row, col, date, cell_format=None): cell_format = self.default_date_format # Store the cell data in the worksheet data table. - self.table[row][col] = cell_datetime_tuple(number, cell_format) + self.table[row][col] = CellDatetimeTuple(number, cell_format) return 0 @@ -1179,7 +1184,7 @@ def _write_boolean(self, row, col, boolean, cell_format=None): value = 0 # Store the cell data in the worksheet data table. - self.table[row][col] = cell_boolean_tuple(value, cell_format) + self.table[row][col] = CellBooleanTuple(value, cell_format) return 0 @@ -1431,7 +1436,7 @@ def _write_rich_string(self, row, col, *args): # Write the string fragment part, with whitespace handling. attributes = [] - if preserve_whitespace(token): + if _preserve_whitespace(token): attributes.append(("xml:space", "preserve")) self.rstring._xml_data_element("t", token, attributes) @@ -1459,7 +1464,7 @@ def _write_rich_string(self, row, col, *args): self._write_single_row(row) # Store the cell data in the worksheet data table. - self.table[row][col] = cell_rich_string_tuple( + self.table[row][col] = CellRichStringTuple( string_index, cell_format, raw_string ) @@ -1639,13 +1644,13 @@ def embed_image(self, row, col, filename, options=None): _, _, digest, - ) = get_image_properties(filename, image_data) + ) = _get_image_properties(filename, image_data) image = [filename, image_type, image_data, description, decorative] image_index = self.embedded_images.get_image_index(image, digest) # Store the cell error and image index in the worksheet data table. - self.table[row][col] = cell_error_tuple("#VALUE!", cell_format, image_index) + self.table[row][col] = CellErrorTuple("#VALUE!", cell_format, image_index) return 0 @@ -1728,12 +1733,12 @@ def insert_chart(self, row, col, chart, options=None): # Ensure a chart isn't inserted more than once. if chart.already_inserted or chart.combined and chart.combined.already_inserted: warn("Chart cannot be inserted in a worksheet more than once.") - return - else: - chart.already_inserted = True + return -2 + + chart.already_inserted = True - if chart.combined: - chart.combined.already_inserted = True + if chart.combined: + chart.combined.already_inserted = True x_offset = options.get("x_offset", 0) y_offset = options.get("y_offset", 0) @@ -1828,8 +1833,10 @@ def set_background(self, filename, is_byte_stream=False): Args: filename: Path and filename for in supported formats. is_byte_stream: File is a stream of bytes. + Returns: - Nothing. + 0: Success. + -1: Image file not found. """ @@ -1840,6 +1847,8 @@ def set_background(self, filename, is_byte_stream=False): self.background_bytes = is_byte_stream self.background_image = filename + return 0 + def set_comments_author(self, author): """ Set the default author of the cell comments. @@ -2000,13 +2009,10 @@ def set_column( return -1 # Set the limits for the outline levels (0 <= x <= 7). - if level < 0: - level = 0 - if level > 7: - level = 7 + level = max(level, 0) + level = min(level, 7) - if level > self.outline_col_level: - self.outline_col_level = level + self.outline_col_level = max(self.outline_col_level, level) # Store the column data. for col in range(first_col, last_col + 1): @@ -2053,6 +2059,7 @@ def autofit(self): Nothing. """ + # pylint: disable=too-many-nested-blocks if self.constant_memory: warn("Autofit is not supported in constant_memory mode.") return @@ -2101,8 +2108,7 @@ def autofit(self): # Handle multi-line strings. for string in string.split("\n"): seg_length = xl_pixel_width(string) - if seg_length > length: - length = seg_length + length = max(length, seg_length) elif cell_type == "Number": # Handle numbers. @@ -2131,7 +2137,7 @@ def autofit(self): else: length = 36 - elif cell_type == "Formula" or cell_type == "ArrayFormula": + elif cell_type in ("Formula", "ArrayFormula"): # Handle formulas. # # We only try to autofit a formula if it has a @@ -2166,8 +2172,7 @@ def autofit(self): width = self._pixels_to_width(pixel_width + 7) # The max column character width in Excel is 255. - if width > 255.0: - width = 255.0 + width = min(width, 255.0) # Add the width to an existing col info structure or add a new one. if self.col_info.get(col_num): @@ -2230,13 +2235,10 @@ def set_row(self, row, height=None, cell_format=None, options=None): height = self.default_row_height # Set the limits for the outline levels (0 <= x <= 7). - if level < 0: - level = 0 - if level > 7: - level = 7 + level = max(level, 0) + level = min(level, 7) - if level > self.outline_row_level: - self.outline_row_level = level + self.outline_row_level = max(self.outline_row_level, level) # Store the row properties. self.set_rows[row] = [height, cell_format, hidden, level, collapsed] @@ -2319,7 +2321,7 @@ def merge_range( # Excel doesn't allow a single cell to be merged if first_row == last_row and first_col == last_col: warn("Can't merge single cell") - return + return -1 # Swap last row/col with first row/col as necessary if first_row > last_row: @@ -2344,14 +2346,15 @@ def merge_range( f"Merge range '{cell_range}' overlaps previous merge " f"range '{previous_range}'." ) - elif self.table_cells.get((row, col)): + + if self.table_cells.get((row, col)): previous_range = self.table_cells.get((row, col)) raise OverlappingRange( f"Merge range '{cell_range}' overlaps previous table " f"range '{previous_range}'." ) - else: - self.merged_cells[(row, col)] = cell_range + + self.merged_cells[(row, col)] = cell_range # Store the merge range. self.merge.append([first_row, first_col, last_row, last_col]) @@ -2593,8 +2596,8 @@ def data_validation(self, first_row, first_col, last_row, last_col, options=None f"'validate' in data_validation()" ) return -2 - else: - options["validate"] = valid_types[options["validate"]] + + options["validate"] = valid_types[options["validate"]] # No action is required for validation type 'any' if there are no # input messages to display. @@ -2647,8 +2650,8 @@ def data_validation(self, first_row, first_col, last_row, last_col, options=None f"'criteria' in data_validation()" ) return -2 - else: - options["criteria"] = criteria_types[options["criteria"]] + + options["criteria"] = criteria_types[options["criteria"]] # 'Between' and 'Not between' criteria require 2 values. if options["criteria"] == "between" or options["criteria"] == "notBetween": @@ -2684,13 +2687,13 @@ def data_validation(self, first_row, first_col, last_row, last_col, options=None if ( options["validate"] in ("date", "time") and options["value"] - and supported_datetime(options["value"]) + and _supported_datetime(options["value"]) ): date_time = self._convert_date_time(options["value"]) # Format date number to the same precision as Excel. options["value"] = f"{date_time:.16g}" - if options["maximum"] and supported_datetime(options["maximum"]): + if options["maximum"] and _supported_datetime(options["maximum"]): date_time = self._convert_date_time(options["maximum"]) options["maximum"] = f"{date_time:.16g}" @@ -2874,10 +2877,10 @@ def conditional_format( f"in conditional_format()" ) return -2 - else: - if options["type"] == "bottom": - options["direction"] = "bottom" - options["type"] = valid_type[options["type"]] + + if options["type"] == "bottom": + options["direction"] = "bottom" + options["type"] = valid_type[options["type"]] # Valid criteria types. criteria_type = { @@ -2924,29 +2927,29 @@ def conditional_format( options["type"] = "cellIs" if "value" in options: - if not supported_datetime(options["value"]): + if not _supported_datetime(options["value"]): warn("Conditional format 'value' must be a datetime object.") return -2 - else: - date_time = self._convert_date_time(options["value"]) - # Format date number to the same precision as Excel. - options["value"] = f"{date_time:.16g}" + + date_time = self._convert_date_time(options["value"]) + # Format date number to the same precision as Excel. + options["value"] = f"{date_time:.16g}" if "minimum" in options: - if not supported_datetime(options["minimum"]): + if not _supported_datetime(options["minimum"]): warn("Conditional format 'minimum' must be a datetime object.") return -2 - else: - date_time = self._convert_date_time(options["minimum"]) - options["minimum"] = f"{date_time:.16g}" + + date_time = self._convert_date_time(options["minimum"]) + options["minimum"] = f"{date_time:.16g}" if "maximum" in options: - if not supported_datetime(options["maximum"]): + if not _supported_datetime(options["maximum"]): warn("Conditional format 'maximum' must be a datetime object.") return -2 - else: - date_time = self._convert_date_time(options["maximum"]) - options["maximum"] = f"{date_time:.16g}" + + date_time = self._convert_date_time(options["maximum"]) + options["maximum"] = f"{date_time:.16g}" # Valid icon styles. valid_icons = { @@ -2986,8 +2989,8 @@ def conditional_format( f"in conditional_format()." ) return -2 - else: - options["icon_style"] = valid_icons[options["icon_style"]] + + options["icon_style"] = valid_icons[options["icon_style"]] # Set the number of icons for the icon style. options["total_icons"] = 3 @@ -3025,6 +3028,7 @@ def conditional_format( self.dxf_priority += 1 # Check for 2010 style data_bar parameters. + # pylint: disable=too-many-boolean-expressions if ( self.use_data_bars_2010 or options.get("data_bar_2010") @@ -3159,8 +3163,8 @@ def conditional_format( options.setdefault("min_color", "#FF7128") options.setdefault("max_color", "#FFEF9C") - options["min_color"] = xl_color(options["min_color"]) - options["max_color"] = xl_color(options["max_color"]) + options["min_color"] = _xl_color(options["min_color"]) + options["max_color"] = _xl_color(options["max_color"]) # Special handling for 3 color scale. if options["type"] == "3_color_scale": @@ -3178,9 +3182,9 @@ def conditional_format( options.setdefault("mid_color", "#FFEB84") options.setdefault("max_color", "#63BE7B") - options["min_color"] = xl_color(options["min_color"]) - options["mid_color"] = xl_color(options["mid_color"]) - options["max_color"] = xl_color(options["max_color"]) + options["min_color"] = _xl_color(options["min_color"]) + options["mid_color"] = _xl_color(options["mid_color"]) + options["max_color"] = _xl_color(options["max_color"]) # Set a default mid value. if "mid_value" not in options: @@ -3218,11 +3222,11 @@ def conditional_format( options.setdefault("bar_axis_position", "") options.setdefault("bar_axis_color", "#000000") - options["bar_color"] = xl_color(options["bar_color"]) - options["bar_border_color"] = xl_color(options["bar_border_color"]) - options["bar_axis_color"] = xl_color(options["bar_axis_color"]) - options["bar_negative_color"] = xl_color(options["bar_negative_color"]) - options["bar_negative_border_color"] = xl_color( + options["bar_color"] = _xl_color(options["bar_color"]) + options["bar_border_color"] = _xl_color(options["bar_border_color"]) + options["bar_axis_color"] = _xl_color(options["bar_axis_color"]) + options["bar_negative_color"] = _xl_color(options["bar_negative_color"]) + options["bar_negative_border_color"] = _xl_color( options["bar_negative_border_color"] ) @@ -3314,14 +3318,15 @@ def add_table(self, first_row, first_col, last_row, last_col, options=None): f"Table range '{cell_range}' overlaps previous " f"table range '{previous_range}'." ) - elif self.merged_cells.get((row, col)): + + if self.merged_cells.get((row, col)): previous_range = self.merged_cells.get((row, col)) raise OverlappingRange( f"Table range '{cell_range}' overlaps previous " f"merge range '{previous_range}'." ) - else: - self.table_cells[(row, col)] = cell_range + + self.table_cells[(row, col)] = cell_range # Valid input parameters. valid_parameter = { @@ -3468,8 +3473,8 @@ def add_table(self, first_row, first_col, last_row, last_col, options=None): if name in seen_names: warn(f"Duplicate header name in add_table(): '{name}'") return -2 - else: - seen_names[name] = True + + seen_names[name] = True col_data["name_format"] = user_data.get("header_format") @@ -3761,7 +3766,7 @@ def add_sparkline(self, row, col, options=None): # Set the sparkline styles. style_id = options.get("style", 0) - style = get_sparkline_style(style_id) + style = _get_sparkline_style(style_id) sparkline["series_color"] = style["series"] sparkline["negative_color"] = style["negative"] @@ -3957,7 +3962,7 @@ def set_tab_color(self, color): Nothing. """ - self.tab_color = xl_color(color) + self.tab_color = _xl_color(color) def protect(self, password="", options=None): """ @@ -4020,7 +4025,8 @@ def unprotect_range(self, cell_range, range_name=None, password=None): password: An optional password string. (undocumented) Returns: - Nothing. + 0: Success. + -1: Parameter error. """ if cell_range is None: @@ -4041,6 +4047,8 @@ def unprotect_range(self, cell_range, range_name=None, password=None): self.protected_ranges.append((cell_range, range_name, password)) + return 0 + @convert_cell_args def insert_button(self, row, col, options=None): """ @@ -4120,7 +4128,7 @@ def set_page_view(self, view=1): """ self.page_view = view - def set_pagebreak_view(self, view=1): + def set_pagebreak_view(self): """ Set the page view mode. @@ -4476,7 +4484,7 @@ def print_area(self, first_row, first_col, last_row, last_col): and last_row == self.xls_rowmax - 1 and last_col == self.xls_colmax - 1 ): - return + return -1 # Build up the print area range "Sheet1!$A$1:$C$13". area = self._convert_name_area(first_row, first_col, last_row, last_col) @@ -4623,9 +4631,9 @@ def ignore_errors(self, options=None): """ if options is None: return -1 - else: - # Copy the user defined options so they aren't modified. - options = options.copy() + + # Copy the user defined options so they aren't modified. + options = options.copy() # Valid input parameters. valid_parameters = { @@ -4694,6 +4702,7 @@ def _initialize(self, init_data): (fd, filename) = tempfile.mkstemp(dir=self.tmpdir) os.close(fd) self.row_data_filename = filename + # pylint: disable=consider-using-with self.row_data_fh = open(filename, mode="w+", encoding="utf-8") # Set as the worksheet filehandle until the file is assembled. @@ -4834,7 +4843,7 @@ def _check_dimensions(self, row, col, ignore_row=False, ignore_col=False): def _convert_date_time(self, dt_obj): # Convert a datetime object to an Excel serial date and time. - return datetime_to_excel_datetime(dt_obj, self.date_1904, self.remove_timezone) + return _datetime_to_excel_datetime(dt_obj, self.date_1904, self.remove_timezone) def _convert_name_area(self, row_num_1, col_num_1, row_num_2, col_num_2): # Convert zero indexed rows and columns to the format required by @@ -4884,7 +4893,7 @@ def _sort_pagebreaks(self, breaks): # 2. Sorts the list. # 3. Removes 0 from the list if present. if not breaks: - return + return [] breaks_set = set(breaks) @@ -4960,10 +4969,9 @@ def _parse_filter_expression(self, expression, tokens): expression_1 = self._parse_filter_tokens(expression, tokens[0:3]) expression_2 = self._parse_filter_tokens(expression, tokens[4:7]) - return expression_1 + [conditional] + expression_2 - else: - return self._parse_filter_tokens(expression, tokens) + + return self._parse_filter_tokens(expression, tokens) def _parse_filter_tokens(self, expression, tokens): # Parse the 3 tokens of a filter expression and return the operator @@ -4999,7 +5007,7 @@ def _parse_filter_tokens(self, expression, tokens): token = token.lower() - if token != "items" and token != "%": + if token not in ("items", "%"): warn( f"The type '{token}' in expression '{expression}' " f"must be either 'items' or '%%'" @@ -5024,7 +5032,7 @@ def _parse_filter_tokens(self, expression, tokens): # Special handling for Blanks/NonBlanks. if re.match("blanks|nonblanks", token.lower()): # Only allow Equals or NotEqual in this context. - if operator != 2 and operator != 5: + if operator not in (2, 5): warn( f"The operator '{tokens[1]}' in expression '{expression}' " f"is not valid in relation to Blanks/NonBlanks'." @@ -5057,17 +5065,17 @@ def _encode_password(self, password): # ECMA-376-4:2016, Office Open XML File Formats — Transitional # Migration Features, Additional attributes for workbookProtection # element (Part 1, §18.2.29). - hash = 0x0000 + digest = 0x0000 for char in password[::-1]: - hash = ((hash >> 14) & 0x01) | ((hash << 1) & 0x7FFF) - hash ^= ord(char) + digest = ((digest >> 14) & 0x01) | ((digest << 1) & 0x7FFF) + digest ^= ord(char) - hash = ((hash >> 14) & 0x01) | ((hash << 1) & 0x7FFF) - hash ^= len(password) - hash ^= 0xCE4B + digest = ((digest >> 14) & 0x01) | ((digest << 1) & 0x7FFF) + digest ^= len(password) + digest ^= 0xCE4B - return f"{hash:X}" + return f"{digest:X}" def _prepare_image( self, @@ -5460,6 +5468,7 @@ def _position_object_pixels( y_abs = 0 # Adjust start column for negative offsets. + # pylint: disable=chained-comparison while x1 < 0 and col_start > 0: x1 += self._size_col(col_start - 1) col_start -= 1 @@ -5470,11 +5479,8 @@ def _position_object_pixels( row_start -= 1 # Ensure that the image isn't shifted off the page at top left. - if x1 < 0: - x1 = 0 - - if y1 < 0: - y1 = 0 + x1 = max(0, x1) + y1 = max(0, y1) # Calculate the absolute x offset of the top-left vertex. if self.col_size_changed: @@ -5637,7 +5643,7 @@ def _comment_params(self, row, col, string, options): params["height"] = default_height # Set the comment background color. - params["color"] = xl_color(params["color"]).lower() + params["color"] = _xl_color(params["color"]).lower() # Convert from Excel XML style color to XML html style color. params["color"] = params["color"].replace("ff", "#", 1) @@ -5886,8 +5892,8 @@ def _prepare_tables(self, table_id, seen): raise DuplicateTableName( f"Duplicate name '{table['name']}' used in worksheet.add_table()." ) - else: - seen[name] = True + + seen[name] = True # Store the link used for the rels file. self.external_table_links.append( @@ -5929,7 +5935,7 @@ def _set_spark_color(self, sparkline, options, user_color): if user_color not in options: return - sparkline[user_color] = {"rgb": xl_color(options[user_color])} + sparkline[user_color] = {"rgb": _xl_color(options[user_color])} def _get_range_data(self, row_start, col_start, row_end, col_end): # Returns a range of data from the worksheet _table to be used in @@ -6017,21 +6023,22 @@ def _get_drawing_rel_index(self, target=None): if target is None: self.drawing_rels_id += 1 return self.drawing_rels_id - elif self.drawing_rels.get(target): + + if self.drawing_rels.get(target): return self.drawing_rels[target] - else: - self.drawing_rels_id += 1 - self.drawing_rels[target] = self.drawing_rels_id - return self.drawing_rels_id + + self.drawing_rels_id += 1 + self.drawing_rels[target] = self.drawing_rels_id + return self.drawing_rels_id def _get_vml_drawing_rel_index(self, target=None): # Get the index used to address a vml drawing rel link. if self.vml_drawing_rels.get(target): return self.vml_drawing_rels[target] - else: - self.vml_drawing_rels_id += 1 - self.vml_drawing_rels[target] = self.vml_drawing_rels_id - return self.vml_drawing_rels_id + + self.vml_drawing_rels_id += 1 + self.vml_drawing_rels[target] = self.vml_drawing_rels_id + return self.vml_drawing_rels_id ########################################################################### # @@ -6136,6 +6143,7 @@ def _opt_reopen(self): # Reopen the row data filehandle in constant_memory mode. if self.row_data_fh_closed: filename = self.row_data_filename + # pylint: disable=consider-using-with self.row_data_fh = open(filename, mode="a+", encoding="utf-8") self.row_data_fh_closed = False self.fh = self.row_data_fh @@ -6324,7 +6332,7 @@ def _write_sheet_view(self): attributes.append(("workbookViewId", 0)) - if self.panes or len(self.selections): + if self.panes or self.selections: self._xml_start_tag("sheetView", attributes) self._write_panes() self._write_selections() @@ -6737,10 +6745,8 @@ def _calculate_spans(self): span_min = col_num span_max = col_num else: - if col_num < span_min: - span_min = col_num - if col_num > span_max: - span_max = col_num + span_min = min(span_min, col_num) + span_max = max(span_max, col_num) if row_num in self.comments: # Calculate spans for comments. @@ -6750,10 +6756,8 @@ def _calculate_spans(self): span_min = col_num span_max = col_num else: - if col_num < span_min: - span_min = col_num - if col_num > span_max: - span_max = col_num + span_min = min(span_min, col_num) + span_max = max(span_max, col_num) if ((row_num + 1) % 16 == 0) or row_num == self.dim_rowmax: span_index = int(row_num / 16) @@ -6867,7 +6871,7 @@ def _write_cell(self, row, col, cell): self._xml_rich_inline_string(string, attributes) else: # Add attribute to preserve leading or trailing whitespace. - preserve = preserve_whitespace(string) + preserve = _preserve_whitespace(string) self._xml_inline_string(string, preserve, attributes) elif type_cell_name == "Formula": @@ -7851,7 +7855,7 @@ def _write_panes(self): # Write the frozen or split elements. panes = self.panes - if not len(panes): + if not panes: return if panes[4] == 2: @@ -7915,7 +7919,7 @@ def _write_freeze_panes(self, row, col, top_row, left_col, pane_type): self._xml_empty_tag("pane", attributes) - def _write_split_panes(self, row, col, top_row, left_col, pane_type): + def _write_split_panes(self, row, col, top_row, left_col, _): # Write the element for split panes. attributes = [] has_selection = 0 @@ -8477,11 +8481,11 @@ def _write_ignored_errors(self): self._xml_end_tag("ignoredErrors") - def _write_ignored_error(self, type, ignored_range): + def _write_ignored_error(self, error_type, ignored_range): # Write the element. attributes = [ ("sqref", ignored_range), - (type, 1), + (error_type, 1), ] self._xml_empty_tag("ignoredError", attributes) diff --git a/xlsxwriter/xmlwriter.py b/xlsxwriter/xmlwriter.py index b2551f34e..dd7e5936c 100644 --- a/xlsxwriter/xmlwriter.py +++ b/xlsxwriter/xmlwriter.py @@ -8,6 +8,8 @@ # Copyright 2013-2024, John McNamara, jmcnamara@cpan.org # +# pylint: disable=dangerous-default-value + # Standard packages. import re from io import StringIO @@ -18,7 +20,7 @@ xml_escapes = re.compile('["&<>\n]') -class XMLwriter(): +class XMLwriter: """ Simple XML writer class. @@ -40,6 +42,7 @@ def _set_xml_writer(self, filename): self.fh = filename else: self.internal_fh = True + # pylint: disable-next=consider-using-with self.fh = open(filename, "w", encoding="utf-8") def _xml_close(self):