diff --git a/.github/scripts/lint_with_pylint.sh b/.github/scripts/lint_with_pylint.sh index 4f259d6..de68081 100755 --- a/.github/scripts/lint_with_pylint.sh +++ b/.github/scripts/lint_with_pylint.sh @@ -28,9 +28,7 @@ else fi echo "[OK] HOSTNAME=${HOSTNAME} OS_NAME=${OS_NAME} OS_VERSION=${OS_VERSION}" -cd src - -pylint k2hash --py3k -r n +pylint src/k2hash exit $? diff --git a/.github/scripts/test_with_unittest.sh b/.github/scripts/test_with_unittest.sh deleted file mode 100755 index 06eac34..0000000 --- a/.github/scripts/test_with_unittest.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/sh -# -# K2hash Python Driver under MIT License -# -# Copyright (c) 2022 Yahoo Japan Corporation -# -# For the full copyright and license information, please view -# the license file that was distributed with this source code. -# -# AUTHOR: Hirotaka Wakabayashi -# CREATE: Tue Feb 08 2022 -# REVISION: -# - -echo $(basename $0) - -if test -f "/etc/os-release"; then - . /etc/os-release - OS_NAME=$ID - OS_VERSION=$VERSION_ID -elif test -f "/etc/centos-release"; then - echo "[OK] /etc/centos-release falling back to CentOS-7" - OS_NAME=centos - OS_VERSION=7 -else - echo "[NO] Unknown OS, neither /etc/os-release nor /etc/centos-release" - exit 1 -fi - -echo "[OK] HOSTNAME=${HOSTNAME} OS_NAME=${OS_NAME} OS_VERSION=${OS_VERSION}" -PYTHON="" -case "${OS_NAME}-${OS_VERSION}" in - ubuntu*|debian*) - PYTHON=$(which python) - ;; - centos*|fedora*) - PYTHON=$(which python3) - ;; -esac - -cd src - -TEST_FILES="test_k2hash.py test_k2hash_package.py test_keyqueue.py test_queue.py" -for TEST_FILE in ${TEST_FILES} -do - ${PYTHON} -m unittest k2hash/tests/${TEST_FILE} - if test $? -ne 0; then - echo "[NO] ${PYTHON} -m unittest k2hash/tests/${TEST_FILE}" - exit 1 - fi -done - -exit 0 - -# -# Local variables: -# tab-width: 4 -# c-basic-offset: 4 -# End: -# vim600: expandtab sw=4 ts=4 fdm=marker -# vim<600: expandtab sw=4 ts=4 -# diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 915975d..7cdcaee 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -31,25 +31,33 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.x", "3.10", "3.9", "3.8", "3.7"] - + python-version: ["3.11", "3.10", "3.9", "3.8"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + architecture: "x64" - name: Install dependencies run: | ./.github/scripts/install_dependencies.sh shell: sh + - name: Install dependencies in GHA + run: | + python -m pip install --upgrade pip + pip install pylint pytest - name: Lint with pylint run: | ./.github/scripts/lint_with_pylint.sh shell: sh - name: Test with unittest run: | - ./.github/scripts/test_with_unittest.sh + python -I -m pip install 'setuptools>=42' wheel + pip install -q build + python -m build + pip3 install dist/k2hash-*.whl + pytest shell: sh - name: Install dependencies for upload pypi package if: startsWith(github.ref, 'refs/tags') @@ -60,7 +68,7 @@ jobs: if: startsWith(github.ref, 'refs/tags') run: python -m build - name: Publish distribution to PyPI - if: ${{ matrix.python-version == '3.x' && startsWith(github.ref, 'refs/tags') }} + if: ${{ matrix.python-version == '3.11' && startsWith(github.ref, 'refs/tags') }} uses: pypa/gh-action-pypi-publish@master with: password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/HISTORY.rst b/HISTORY.rst index 3ac6634..325235b 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -2,6 +2,11 @@ History ======= +1.0.1 (2023-09-26) +------------------- + +* Updates the CI settings. + 1.0.0 (2022-02-07) ------------------- diff --git a/docs/conf.py b/docs/conf.py index d95c251..fd883ad 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -36,7 +36,7 @@ author = 'Hirotaka Wakabayashi, Takeshi Nakatani' # The full version, including alpha/beta/rc tags -release = '1.0.0' +release = '1.0.1' # -- General configuration --------------------------------------------------- diff --git a/docs/locales/ja/LC_MESSAGES/history.po b/docs/locales/ja/LC_MESSAGES/history.po index 820d0cd..2903e71 100644 --- a/docs/locales/ja/LC_MESSAGES/history.po +++ b/docs/locales/ja/LC_MESSAGES/history.po @@ -1,39 +1,43 @@ # -*- coding: utf-8 -*- # # K2hash Python Driver under MIT License -# # Copyright (c) 2022 Yahoo Japan Corporation -# # For the full copyright and license information, please view # the license file that was distributed with this source code. -# # AUTHOR: Hirotaka Wakabayashi # CREATE: Tue Feb 08 2022 # REVISION: -# #, fuzzy msgid "" msgstr "" "Project-Id-Version: k2hash \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-02-07 07:26+0000\n" +"POT-Creation-Date: 2023-09-26 14:11+0900\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.9.1\n" +"Generated-By: Babel 2.12.1\n" #: ../../../HISTORY.rst:3 msgid "History" msgstr "歴史" #: ../../../HISTORY.rst:6 -msgid "1.0.0 (2022-02-07)" +msgid "1.0.1 (2023-09-26)" msgstr "" #: ../../../HISTORY.rst:8 +msgid "Updates the CI settings." +msgstr "CIの設定を更新" + +#: ../../../HISTORY.rst:11 +msgid "1.0.0 (2022-02-07)" +msgstr "" + +#: ../../../HISTORY.rst:13 msgid "First release on PyPI." msgstr "最初のリリース" diff --git a/docs/locales/ja/LC_MESSAGES/k2hash.po b/docs/locales/ja/LC_MESSAGES/k2hash.po index bcaa290..6a0229d 100644 --- a/docs/locales/ja/LC_MESSAGES/k2hash.po +++ b/docs/locales/ja/LC_MESSAGES/k2hash.po @@ -1,29 +1,25 @@ # -*- coding: utf-8 -*- # # K2hash Python Driver under MIT License -# # Copyright (c) 2022 Yahoo Japan Corporation -# # For the full copyright and license information, please view # the license file that was distributed with this source code. -# # AUTHOR: Hirotaka Wakabayashi # CREATE: Tue Feb 08 2022 # REVISION: -# #, fuzzy msgid "" msgstr "" "Project-Id-Version: k2hash \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-03-01 19:15+0900\n" +"POT-Creation-Date: 2023-09-26 14:11+0900\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Hirotaka Wakabayashi \n" "Language-Team: Antpickax \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.9.1\n" +"Generated-By: Babel 2.12.1\n" #: ../../k2hash.rst:2 k2hash:1 of msgid "k2hash package" @@ -34,15 +30,15 @@ msgid "Submodules" msgstr "サブモジュール" #: ../../k2hash.rst:8 -msgid "k2hash.k2hash モジュール" -msgstr "" +msgid "k2hash.k2hash module" +msgstr "k2hashモジュール" #: k2hash.k2hash:1 k2hash.keyqueue:1 k2hash.queue:1 of msgid "K2hash Python Driver under MIT License" msgstr "" -#: k2hash.k2hash.K2hash:1 k2hash.k2hash.K2hashIterator:1 -#: k2hash.keyqueue.KeyQueue:1 k2hash.queue.Queue:1 of +#: k2hash.basequeue.BaseQueue:1 k2hash.k2hash.K2hash:1 +#: k2hash.k2hash.K2hashIterator:1 of msgid "Bases: :py:class:`object`" msgstr "" @@ -116,9 +112,8 @@ msgstr "トランザクションログファイル記述子を取得します。 msgid "Gets the number of transaction thread pool." msgstr "トランザクション用スレッド数を取得します。" -#: k2hash.K2hash.handle:1 k2hash.KeyQueue.handle:1 k2hash.Queue.handle:1 -#: k2hash.k2hash.K2hash.handle:1 k2hash.keyqueue.KeyQueue.handle:1 -#: k2hash.queue.Queue.handle:1 of +#: k2hash.BaseQueue.handle:1 k2hash.K2hash.handle:1 +#: k2hash.k2hash.K2hash.handle:1 of msgid "Returns a Queue handle." msgstr "キューハンドルを返します。" @@ -156,7 +151,7 @@ msgstr "キーを削除します。" #: k2hash.k2hash.K2hash.remove_subkeys:1 of msgid "Removes subkeys from the key." -msgstr "サブキーを削除します。 +msgstr "サブキーを削除します" #: k2hash.k2hash.K2hash.rename:1 of msgid "Renames a key with a new key." @@ -206,6 +201,10 @@ msgstr "k2hashのiteratorを実装しています。" msgid "k2hash.keyqueue module" msgstr "" +#: k2hash.keyqueue.KeyQueue:1 k2hash.queue.Queue:1 of +msgid "Bases: :py:class:`~k2hash.basequeue.BaseQueue`" +msgstr "" + #: k2hash.keyqueue.KeyQueue:1 of msgid "" "KeyQueue class provides methods to handle key/value pairs in k2hash hash " @@ -247,7 +246,7 @@ msgstr "キューから要素を削除します。" #: ../../k2hash.rst:24 msgid "k2hash.queue module" -msgstr "" +msgstr "k2hash.queue モジュール" #: k2hash.queue.Queue:1 of msgid "" @@ -260,7 +259,7 @@ msgid "Module contents" msgstr "モジュール" #: k2hash.AttrPack:1 k2hash.KeyPack:1 of -msgid "Bases: :py:class:`_ctypes.Structure`" +msgid "Bases: :py:class:`~_ctypes.Structure`" msgstr "" #: k2hash.AttrPack:1 of @@ -273,8 +272,15 @@ msgstr "Attr構造体" msgid "Structure/Union member" msgstr "構造体のメンバー" +#: k2hash.basequeue.BaseQueue:1 of +#, fuzzy +msgid "" +"Baseueue class provides methods to handle key/value pairs in k2hash hash " +"database." +msgstr "Queueクラスは、キュー操作メソッドを提供します。" + #: k2hash.DumpLevel:1 k2hash.LogLevel:1 k2hash.OpenFlag:1 k2hash.TimeUnit:1 of -msgid "Bases: :py:class:`enum.Enum`" +msgid "Bases: :py:class:`~enum.Enum`" msgstr "" #: k2hash.DumpLevel:1 of @@ -297,3 +303,12 @@ msgstr "ファイルオープン用フラグ" msgid "k2hash time units" msgstr "時間単位" +#~ msgid "k2hash.k2hash モジュール" +#~ msgstr "" + +#~ msgid "Bases: :py:class:`_ctypes.Structure`" +#~ msgstr "" + +#~ msgid "Bases: :py:class:`enum.Enum`" +#~ msgstr "" + diff --git a/pylintrc b/pylintrc deleted file mode 100644 index b54a488..0000000 --- a/pylintrc +++ /dev/null @@ -1,563 +0,0 @@ -# -*- coding: utf-8 -*- -# -# K2hash Python Driver under MIT License -# -# Copyright (c) 2022 Yahoo Japan Corporation -# -# For the full copyright and license information, please view -# the license file that was distributed with this source code. -# -# AUTHOR: Hirotaka Wakabayashi -# CREATE: Tue Feb 08 2022 -# REVISION: -# - -[MASTER] - -# 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= - -# Specify a score threshold to be exceeded before program exits with error. -fail-under=10 - -# Files or directories to be skipped. They should be base names, not paths. -ignore=CVS - -# Files or directories matching the regex patterns are skipped. The regex -# matches against base names, not paths. -ignore-patterns= - -# Add files or directories matching the regex patterns to the ignore-list. -# The regex matches against paths. NOTE: Because "\" represents the directory -# delimiter on Windows systems it can't be used as the escape character -# in these regular expressions. -ignore-paths= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the -# number of processors available to use. -jobs=1 - -# Control the amount of potential inferred values when inferring a single -# object. This can help the performance when dealing with large functions or -# complex, nested conditions. -limit-inference-results=100 - -# List of plugins (as comma separated values of python module names) to load, -# usually to register additional checkers. -load-plugins= - -# Pickle collected data for later comparisons. -persistent=yes - -# 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 - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. -confidence= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once). You can also use "--disable=all" to -# disable everything first and then re-enable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use "--disable=all --enable=classes -# --disable=W". -disable=raw-checker-failed, - bad-inline-option, - locally-disabled, - file-ignored, - suppressed-message, - useless-suppression, - deprecated-pragma, - use-symbolic-message-instead, - too-many-arguments, - too-many-instance-attributes, - too-many-public-methods, - consider-using-f-string, - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -enable=c-extension-no-member - - -[REPORTS] - -# Python expression which should return a score less than or equal to 10. You -# have access to the variables 'fatal', 'error', 'warning', 'refactor', 'convention' and 'info' -# which contain the number of messages in each category, as well as 'statement' -# which is the total number of statements analyzed. This score is used by the -# global evaluation report (RP0004). -evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details. -#msg-template= - -# Set the output format. Available formats are text, parseable, colorized, json -# and msvs (visual studio). You can also give a reporter class, e.g. -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Tells whether to display a full report or only the messages. -reports=no - -# Activate the evaluation score. -score=yes - - -[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 - - -[LOGGING] - -# Format style used to check logging format string. `old` means using % -# formatting and `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 - - -[SPELLING] - -# Limits count of emitted suggestions for spelling mistakes. -max-spelling-suggestions=4 - -# Spelling dictionary name. Available dictionaries: none. To make it work, -# install the python-enchant package. -spelling-dict= - -# List of comma separated words that should 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 - - -[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= - - -[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 missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# 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 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 - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis). It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= - -# 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 - -# 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 strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_, - _cb - -# A regular expression matching the name of dummy variables (i.e. expected to -# not be used). -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore. -ignored-argument-names=_.*|^ignored_|^unused_ - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io - - -[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 - -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -no-space-check=trailing-comma, - dict-separator - -# 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 - - -[SIMILARITIES] - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - -# Minimum lines number of a similarity. -min-similarity-lines=4 - - -[BASIC] - -# Naming style matching correct argument names. -argument-naming-style=snake_case - -# Regular expression matching correct argument names. Overrides argument- -# 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. -#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. -#class-attribute-rgx= - -# Naming style matching correct class names. -class-naming-style=PascalCase - -# Regular expression matching correct class names. Overrides class-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. -#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. -#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. -#inlinevar-rgx= - -# Naming style matching correct method names. -method-naming-style=snake_case - -# Regular expression matching correct method names. Overrides method-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. -#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 - -# Naming style matching correct variable names. -variable-naming-style=snake_case - -# Regular expression matching correct variable names. Overrides variable- -# naming-style. -#variable-rgx= - - -[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 - - -[IMPORTS] - -# List of modules that can be imported at any level, not just the top level -# one. -allow-any-import-level= - -# Allow wildcard imports from modules that define __all__. -allow-wildcard-with-all=no - -# 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 - -# Deprecated modules which should not be used, separated by a comma. -deprecated-modules=optparse,tkinter.tix - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled). -ext-import-graph= - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled). -import-graph= - -# Create a graph of internal dependencies in 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= - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__, - __new__, - setUp, - __post_init__ - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict, - _fields, - _replace, - _source, - _make - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=cls - - -[DESIGN] - -# 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 being caught. Defaults to -# "BaseException, Exception". -overgeneral-exceptions=BaseException, - Exception - -# -# Local variables: -# tab-width: 4 -# c-basic-offset: 4 -# End: -# vim600: expandtab sw=4 ts=4 fdm=marker -# vim<600: expandtab sw=4 ts=4 -# - diff --git a/pyproject.toml b/pyproject.toml index c0cb250..9a0f2fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,565 @@ requires = [ ] build-backend = "setuptools.build_meta" +[tool.tox] +legacy_tox_ini = """ + [tox] + requires = tox>=4 + env_list = lint, type, py{38,39,310,311} + skip_missing_interpreters = True + + [testenv] + deps = pytest + commands = pytest + package = wheel + + [testenv:lint] + deps = pylint + commands = pylint src/k2hash + + [testenv:type] + description = run type checks + deps = + mypy>=0.991 + commands = + mypy {posargs:src} +""" + +[tool.pylint.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 = + +# Clear in-memory caches upon conclusion of linting. Useful if running pylint in +# a server-like mode. +# clear-cache-post-run = + +# 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.0 + +# 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 (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 = true + +# Minimum Python version to use for version dependent checks. Will default to the +# version used to run pylint. +py-version = "3.9" + +# Discover python modules and packages in the file system subtree. +# recursive = + +# 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 = true + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +# unsafe-load-any-extension = + +[tool.pylint.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 = + +# 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 = + +[tool.pylint.classes] +# Warn about protected attribute access inside special methods +# check-protected-access-in-special-methods = + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods = ["__init__", "__new__", "setUp", "__post_init__"] + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected = ["_asdict", "_fields", "_replace", "_source", "_make"] + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg = ["cls"] + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg = ["cls"] + +[tool.pylint.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 + +[tool.pylint.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 = + +# Allow the body of an if to be on the same line as the test if there is no else. +# single-line-if-stmt = + +[tool.pylint.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 = + +# Allow wildcard imports from modules that define __all__. +# allow-wildcard-with-all = + +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules = ["optparse", "tkinter.tix"] + +# 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 = + +[tool.pylint.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"] + +[tool.pylint."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", "INFERENCE", "INFERENCE_FAILURE", "UNDEFINED"] + +# Disable the message, report, category or checker with the given id(s). You can +# either give multiple identifiers separated by comma (,) or put this option +# multiple times (only on the command line, not in the configuration file where +# it should appear only once). You can also use "--disable=all" to disable +# everything first and then re-enable specific checks. For example, if you want +# to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +disable = ["raw-checker-failed", "bad-inline-option", "locally-disabled", "file-ignored", "suppressed-message", "useless-suppression", "deprecated-pragma", "use-symbolic-message-instead", "too-many-arguments", "too-many-instance-attributes", "too-many-public-methods", "consider-using-f-string", "cyclic-import", "duplicate-code"] + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where it +# should appear only once). See also the "--disable" option for examples. +enable = ["c-extension-no-member"] + +[tool.pylint.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"] + +[tool.pylint.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 = + +[tool.pylint.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"] + +disable = ["cyclic-import", "duplicate-code"] + +[tool.pylint.reports] +# Python expression which should return a score less than or equal to 10. You +# have access to the variables 'fatal', 'error', 'warning', 'refactor', +# 'convention', and 'info' which contain the number of messages in each category, +# as well as 'statement' which is the total number of statements analyzed. This +# score is used by the global evaluation report (RP0004). +evaluation = "max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10))" + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details. +# msg-template = + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio). You can also give a reporter class, e.g. +# mypackage.mymodule.MyReporterClass. +# output-format = + +# Tells whether to display a full report or only the messages. +# reports = + +# Activate the evaluation score. +score = true + +[tool.pylint.similarities] +# Comments are removed from the similarity computation +ignore-comments = true + +# Docstrings are removed from the similarity computation +ignore-docstrings = true + +# Imports are removed from the similarity computation +# ignore-imports = + +# Signatures are removed from the similarity computation +ignore-signatures = true + +# Minimum lines number of a similarity. +min-similarity-lines = 32 + +[tool.pylint.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 = + +[tool.pylint.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 missing members accessed in mixin class should be ignored. A +# class is considered mixin if its name matches the mixin-class-rgx option. +# Tells whether to warn about missing members when the owner of the attribute is +# inferred to be None. +ignore-none = true + +# 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 = true + +# 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"] + +# 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 = true + +# 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 = + +[tool.pylint.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 = true + +# 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 = + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules = ["six.moves", "past.builtins", "future.builtins", "builtins", "io"] + # # Local variables: # tab-width: 4 diff --git a/setup.cfg b/setup.cfg index 2ee1be4..745b235 100644 --- a/setup.cfg +++ b/setup.cfg @@ -17,7 +17,7 @@ license_files = LICENSE name = k2hash -version = 1.0.0 +version = 1.0.1 author = Hirotaka Wakabayashi author_email = hiwakaba@yahoo-corp.jp description = A module for k2hash from antpickax diff --git a/src/k2hash/__init__.py b/src/k2hash/__init__.py index 8baf90e..2e6a494 100644 --- a/src/k2hash/__init__.py +++ b/src/k2hash/__init__.py @@ -17,48 +17,66 @@ from __future__ import absolute_import __all__ = [ - 'K2hash', 'Queue', 'KeyQueue', 'K2hashIterator', 'LogLevel', 'KeyPack', - 'AttrPack', 'OpenFlag', 'DumpLevel', 'LogLevel', 'TimeUnit' + "K2hash", + "Queue", + "BaseQueue", + "KeyQueue", + "K2hashIterator", + "LogLevel", + "KeyPack", + "AttrPack", + "OpenFlag", + "DumpLevel", + "LogLevel", + "TimeUnit", ] -from typing import List, Set, Dict, Tuple, Optional, Union # noqa: pylint: disable=unused-import - import ctypes +import logging +import sys +from ctypes import ( + POINTER, + Structure, + c_bool, + c_char_p, + c_int, + c_long, + c_size_t, + c_ubyte, + c_uint64, + c_ulong, +) from ctypes.util import find_library -from ctypes import c_bool, c_ubyte, c_size_t, c_int, c_uint64, c_long -from ctypes import c_ulong, c_char_p, POINTER, Structure from enum import Enum -import logging -from logging.handlers import TimedRotatingFileHandler from logging import StreamHandler -import sys +from logging.handlers import TimedRotatingFileHandler +from typing import List # noqa: pylint: disable=unused-import +from typing import Dict, Optional, Set, Tuple, Union LOG = logging.getLogger(__name__) # https://docs.python.org/3/library/ctypes.html#incomplete-types class FILE(Structure): # noqa: pylint:disable=too-few-public-methods - """C FILE structure - """ + """C FILE structure""" # https://docs.python.org/3/library/ctypes.html#incomplete-types class time_t(Structure): # noqa: pylint:disable=too-few-public-methods,invalid-name - """C time_t structure - """ + """C time_t structure""" # KeyPack structure # See: https://github.com/yahoojapan/k2hash/blob/master/lib/k2hash.h#L75 # # typedef struct k2h_key_pack{ -# unsigned char* pkey; -# size_t length; +# unsigned char* pkey; +# size_t length; # }K2HKEYPCK, *PK2HKEYPCK; # class KeyPack(Structure): # noqa: pylint:disable=too-few-public-methods - """C KeyPack structure - """ + """C KeyPack structure""" + _fields_ = [("pkey", POINTER(c_ubyte)), ("length", c_size_t)] @@ -66,22 +84,26 @@ class KeyPack(Structure): # noqa: pylint:disable=too-few-public-methods # See: https://github.com/yahoojapan/k2hash/blob/master/lib/k2hash.h#L81 # # typedef struct k2h_attr_pack{ -# unsigned char* pkey; -# size_t keylength; -# unsigned char* pval; -# size_t vallength; +# unsigned char* pkey; +# size_t keylength; +# unsigned char* pval; +# size_t vallength; # }K2HATTRPCK, *PK2HATTRPCK; # class AttrPack(Structure): # noqa: pylint:disable=too-few-public-methods - """C Attr structure - """ - _fields_ = [("pkey", POINTER(c_ubyte)), ("keylength", c_size_t), - ("pval", POINTER(c_ubyte)), ("vallength", c_size_t)] + """C Attr structure""" + + _fields_ = [ + ("pkey", POINTER(c_ubyte)), + ("keylength", c_size_t), + ("pval", POINTER(c_ubyte)), + ("vallength", c_size_t), + ] class OpenFlag(Enum): - """k2hash file open flags - """ + """k2hash file open flags""" + READ = 1 EDIT = 2 TEMPFILE = 3 @@ -89,8 +111,8 @@ class OpenFlag(Enum): class TimeUnit(Enum): - """k2hash time units - """ + """k2hash time units""" + DAYS = 1 HOURS = 2 MILLISECONDS = 3 @@ -99,8 +121,8 @@ class TimeUnit(Enum): class DumpLevel(Enum): - """k2hash file status information - """ + """k2hash file status information""" + # Dump headers HEADER = 1 # Dump headers and hash tables @@ -114,8 +136,8 @@ class DumpLevel(Enum): class LogLevel(Enum): - """k2hash log level - """ + """k2hash log level""" + # Silent disables logging. SILENT = 1 # logs on errors @@ -140,8 +162,8 @@ def _init_library_handle(): # Loads libc and libk2hash and ... result = {} - result['c'] = _load_libc() - result['k2hash'] = _load_libk2hash() + result["c"] = _load_libc() + result["k2hash"] = _load_libk2hash() _HANDLE = result return result @@ -157,7 +179,7 @@ def _load_libc(): def _load_libk2hash(): # noqa: pylint: disable=too-many-statements ret = ctypes.cdll.LoadLibrary(find_library("k2hash")) - if ret._name is None: + if ret is None: return None # Defines prototypes for python code @@ -175,10 +197,7 @@ def _load_libk2hash(): # noqa: pylint: disable=too-many-statements ret.k2h_find_next.argtypes = [c_uint64] # bool k2h_find_get_key(k2h_find_h findhandle, unsigned char** ppkey, size_t* pkeylength) ret.k2h_find_get_key.restype = c_uint64 - ret.k2h_find_get_key.argtypes = [ - c_uint64, POINTER(c_char_p), - POINTER(c_size_t) - ] + ret.k2h_find_get_key.argtypes = [c_uint64, POINTER(c_char_p), POINTER(c_size_t)] # # 3. keyqueue API # @@ -193,8 +212,11 @@ def _load_libk2hash(): # noqa: pylint: disable=too-many-statements # const time_t* expire) ret.k2h_keyq_str_push_keyval_wa.restype = c_bool ret.k2h_keyq_str_push_keyval_wa.argtypes = [ - c_uint64, c_char_p, c_char_p, c_char_p, - POINTER(c_long) + c_uint64, + c_char_p, + c_char_p, + c_char_p, + POINTER(c_long), ] # bool k2h_keyq_dump(k2h_keyq_h qhandle, FILE* stream) ret.k2h_keyq_dump.restype = c_bool @@ -211,7 +233,9 @@ def _load_libk2hash(): # noqa: pylint: disable=too-many-statements ret.k2h_keyq_str_read_keyval_wp.argtypes = [ c_uint64, POINTER(c_char_p), - POINTER(c_char_p), c_int, c_char_p + POINTER(c_char_p), + c_int, + c_char_p, ] # bool k2h_keyq_empty(k2h_keyq_h qhandle) ret.k2h_keyq_empty.restype = c_bool @@ -220,8 +244,10 @@ def _load_libk2hash(): # noqa: pylint: disable=too-many-statements # k2h_keyq_h keyqhandle, char** ppkey, char** ppval, const char* encpass) ret.k2h_keyq_str_pop_keyval_wp.restype = c_bool ret.k2h_keyq_str_pop_keyval_wp.argtypes = [ - c_uint64, POINTER(c_char_p), - POINTER(c_char_p), c_char_p + c_uint64, + POINTER(c_char_p), + POINTER(c_char_p), + c_char_p, ] # bool k2h_keyq_remove(k2h_keyq_h qhandle, int count) ret.k2h_keyq_remove.restype = c_bool @@ -238,9 +264,12 @@ def _load_libk2hash(): # noqa: pylint: disable=too-many-statements # int attrspckcnt, const char* encpass, const time_t* expire) ret.k2h_q_str_push_wa.restype = c_bool ret.k2h_q_str_push_wa.argtypes = [ - c_uint64, c_char_p, - POINTER(AttrPack), c_int, c_char_p, - POINTER(c_ulong) + c_uint64, + c_char_p, + POINTER(AttrPack), + c_int, + c_char_p, + POINTER(c_ulong), ] # bool k2h_q_str_push(k2h_q_h qhandle, const char* pval) ret.k2h_q_str_push.restype = c_bool @@ -259,7 +288,9 @@ def _load_libk2hash(): # noqa: pylint: disable=too-many-statements ret.k2h_q_read_wp.argtypes = [ c_uint64, POINTER(c_char_p), - POINTER(c_size_t), c_int, c_char_p + POINTER(c_size_t), + c_int, + c_char_p, ] # bool k2h_q_empty(k2h_q_h qhandle) ret.k2h_q_empty.restype = c_bool @@ -294,7 +325,13 @@ def _load_libk2hash(): # noqa: pylint: disable=too-many-statements # k2h_h handle, const unsigned char* pkey, size_t keylength, const unsigned char* pattrkey, # size_t attrkeylength, const unsigned char* pattrval, size_t attrvallength) ret.k2h_add_attr.argtypes = [ - c_uint64, c_char_p, c_size_t, c_char_p, c_size_t, c_char_p, c_size_t + c_uint64, + c_char_p, + c_size_t, + c_char_p, + c_size_t, + c_char_p, + c_size_t, ] ret.k2h_add_attr.restype = c_bool @@ -303,16 +340,28 @@ def _load_libk2hash(): # noqa: pylint: disable=too-many-statements # k2h_h handle, const unsigned char* pkey, size_t keylength, const unsigned char* psubkey, # size_t skeylength, const unsigned char* pval, size_t vallength) ret.k2h_add_subkey.argtypes = [ - c_uint64, c_char_p, c_size_t, c_char_p, c_size_t, c_char_p, c_size_t + c_uint64, + c_char_p, + c_size_t, + c_char_p, + c_size_t, + c_char_p, + c_size_t, ] ret.k2h_add_subkey.restype = c_bool # bool k2h_add_subkey_wa(k2h_h handle, const unsigned char* pkey, size_t keylength, # const unsigned char* psubkey, size_t skeylength, const unsigned char* pval, # size_t vallength, const char* pass, const time_t* expire) ret.k2h_add_subkey_wa.argtypes = [ - c_uint64, c_char_p, c_size_t, c_char_p, c_size_t, c_char_p, c_size_t, + c_uint64, + c_char_p, + c_size_t, + c_char_p, + c_size_t, + c_char_p, + c_size_t, c_char_p, - POINTER(c_ulong) + POINTER(c_ulong), ] ret.k2h_add_subkey_wa.restype = c_bool @@ -358,19 +407,13 @@ def _load_libk2hash(): # noqa: pylint: disable=too-many-statements # get attrs API # PK2HATTRPCK k2h_get_direct_attrs(k2h_h handle, const unsigned char* pkey, # size_t keylength, int* pattrspckcnt) - ret.k2h_get_direct_attrs.argtypes = [ - c_uint64, c_char_p, c_size_t, - POINTER(c_int) - ] + ret.k2h_get_direct_attrs.argtypes = [c_uint64, c_char_p, c_size_t, POINTER(c_int)] ret.k2h_get_direct_attrs.restype = POINTER(AttrPack) # get subkeys API # PK2HKEYPCK k2h_get_direct_subkeys(k2h_h handle, const unsigned char* pkey, # size_t keylength, int* pskeypckcnt) - ret.k2h_get_direct_subkeys.argtypes = [ - c_uint64, c_char_p, c_size_t, - POINTER(c_int) - ] + ret.k2h_get_direct_subkeys.argtypes = [c_uint64, c_char_p, c_size_t, POINTER(c_int)] ret.k2h_get_direct_subkeys.restype = POINTER(KeyPack) # get transaction API @@ -392,16 +435,21 @@ def _load_libk2hash(): # noqa: pylint: disable=too-many-statements ret.k2h_open_mem.argtypes = [c_int, c_int, c_int, c_int] ret.k2h_open.restype = c_uint64 ret.k2h_open.argtypes = [ - c_char_p, c_bool, c_bool, c_bool, c_int, c_int, c_int, c_int + c_char_p, + c_bool, + c_bool, + c_bool, + c_int, + c_int, + c_int, + c_int, ] ret.k2h_open_rw.restype = c_uint64 ret.k2h_open_rw.argtypes = [c_char_p, c_bool, c_int, c_int, c_int, c_int] ret.k2h_open_ro.restype = c_uint64 ret.k2h_open_ro.argtypes = [c_char_p, c_bool, c_int, c_int, c_int, c_int] ret.k2h_open_tempfile.restype = c_uint64 - ret.k2h_open_tempfile.argtypes = [ - c_char_p, c_bool, c_int, c_int, c_int, c_int - ] + ret.k2h_open_tempfile.argtypes = [c_char_p, c_bool, c_int, c_int, c_int, c_int] # print API # bool k2h_print_attr_version(k2h_h handle, FILE* stream) @@ -449,9 +497,10 @@ def _load_libk2hash(): # noqa: pylint: disable=too-many-statements ret.k2h_set_common_attr.argtypes = [ c_uint64, POINTER(c_bool), - POINTER(c_bool), c_char_p, POINTER(c_bool), - POINTER(c_ulong) + c_char_p, + POINTER(c_bool), + POINTER(c_ulong), ] ret.k2h_set_common_attr.restype = c_bool @@ -468,8 +517,11 @@ def _load_libk2hash(): # noqa: pylint: disable=too-many-statements # bool k2h_set_str_value_wa(k2h_h handle, const char* pkey, const char* pval, const char* pass, # const time_t* expire) ret.k2h_set_str_value_wa.argtypes = [ - c_uint64, c_char_p, c_char_p, c_char_p, - POINTER(c_ulong) + c_uint64, + c_char_p, + c_char_p, + c_char_p, + POINTER(c_ulong), ] ret.k2h_set_str_value_wa.restype = c_bool @@ -480,8 +532,14 @@ def _load_libk2hash(): # noqa: pylint: disable=too-many-statements # const unsigned char* pprefix, size_t prefixlen, const unsigned char* pparam, size_t paramlen, # const time_t* expire) ret.k2h_transaction_param_we.argtypes = [ - c_uint64, c_bool, c_char_p, c_char_p, c_size_t, c_char_p, c_size_t, - POINTER(c_ulong) + c_uint64, + c_bool, + c_char_p, + c_char_p, + c_size_t, + c_char_p, + c_size_t, + POINTER(c_ulong), ] ret.k2h_transaction_param_we.restype = c_bool @@ -498,28 +556,26 @@ def _load_libk2hash(): # noqa: pylint: disable=too-many-statements # Gets library handler def get_library_handle(): - """Gets C library handles - """ + """Gets C library handles""" return _init_library_handle() # Configures logger using std logging. default puts to stderr in warning. -def _configure_logger(log_file='sys.stderr', log_level=logging.WARNING): +def _configure_logger(log_file="sys.stderr", log_level=logging.WARNING): LOG.setLevel(log_level) # 2. formatter formatter = logging.Formatter( - '%(asctime)-15s %(levelname)s %(name)s:%(lineno)d %(message)s' + "%(asctime)-15s %(levelname)s %(name)s:%(lineno)d %(message)s" ) # hardcoding # 3. log_file if log_file and isinstance(log_file, str): - if log_file != 'sys.stderr': + if log_file != "sys.stderr": # Add the log message handler to the logger - handler = TimedRotatingFileHandler(log_file, - when='midnight', - encoding='UTF-8', - backupCount=31) + handler = TimedRotatingFileHandler( + log_file, when="midnight", encoding="UTF-8", backupCount=31 + ) handler.setFormatter(formatter) LOG.addHandler(handler) return @@ -532,8 +588,7 @@ def _configure_logger(log_file='sys.stderr', log_level=logging.WARNING): # Configures the loglevel. def set_log_level(log_level): - """Sets the log level - """ + """Sets the log level""" LOG.setLevel(log_level) @@ -543,9 +598,10 @@ def set_log_level(log_level): # # import k2hash modules # -from k2hash.k2hash import K2hash, K2hashIterator -from k2hash.queue import Queue -from k2hash.keyqueue import KeyQueue +from k2hash.k2hash import K2hash, K2hashIterator # noqa: pylint:disable=wrong-import-position +from k2hash.basequeue import BaseQueue # noqa: pylint:disable=wrong-import-position +from k2hash.keyqueue import KeyQueue # noqa: pylint:disable=wrong-import-position +from k2hash.queue import Queue # noqa: pylint:disable=wrong-import-position # # Local variables: diff --git a/src/k2hash/basequeue.py b/src/k2hash/basequeue.py new file mode 100644 index 0000000..a7c4cab --- /dev/null +++ b/src/k2hash/basequeue.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- +# +# K2hash Python Driver under MIT License +# +# Copyright (c) 2022 Yahoo Japan Corporation +# +# For the full copyright and license information, please view +# the license file that was distributed with this source code. +# +# AUTHOR: Hirotaka Wakabayashi +# CREATE: Tue Feb 08 2022 +# REVISION: +# +"""K2hash Python Driver under MIT License""" +from __future__ import absolute_import + +import logging + +from k2hash import K2hash + +LOG = logging.getLogger(__name__) + + +class BaseQueue: # noqa: pylint: disable=too-many-instance-attributes + """ + Baseueue class provides methods to handle key/value pairs in k2hash hash database. + """ + + def __init__( # noqa: pylint: disable=too-many-arguments + self, k2h, fifo=True, prefix=None, password=None, expire_duration=None + ): + """ + Initialize a new BaseQueue instnace. + """ + if not isinstance(k2h, K2hash): + raise TypeError("k2h should be a K2hash object") + self._k2h_handle = k2h.handle + self._libc = k2h.libc + self._libk2hash = k2h.libk2hash + + if fifo and not isinstance(fifo, bool): + raise TypeError("fifo should be a boolean object") + self._fifo = fifo + if prefix and not isinstance(prefix, str): + raise TypeError("prefix should be a string object") + self._prefix = prefix + if password and not isinstance(password, str): + raise TypeError("password should be a string object") + self._password = password + if expire_duration and not isinstance(expire_duration, int): + raise TypeError("expire_duration should be a boolean object") + if expire_duration and expire_duration <= 0: + raise ValueError("expire_duration should not be positive") + self._expire_duration = expire_duration + + # initializes self._handle, which should be set in subclasses + self._handle = K2hash.K2H_INVALID_HANDLE + + @property + def handle(self): + """Returns a Queue handle.""" + return self._handle + + def __repr__(self): + """Returns full of members as a string.""" + attrs = [] + for attr in [ + "_handle", + "_k2h_handle", + "_libk2hash", + "_fifo", + "_prefix", + "_expire_duration", + ]: # should be hardcoded. + val = getattr(self, attr) + if val: + attrs.append((attr, repr(val))) + else: + attrs.append((attr, "")) + values = ", ".join(["%s=%s" % i for i in attrs]) # noqa: pylint:disable=consider-using-f-string + return f"<_{self.__class__.__name__} " + values + ">" + +# +# Local variables: +# tab-width: 4 +# c-basic-offset: 4 +# End: +# vim600: expandtab sw=4 ts=4 fdm=marker +# vim<600: expandtab sw=4 ts=4 +# diff --git a/src/k2hash/k2hash.py b/src/k2hash/k2hash.py index 61832a0..8f3b23a 100644 --- a/src/k2hash/k2hash.py +++ b/src/k2hash/k2hash.py @@ -15,21 +15,23 @@ from __future__ import absolute_import import ctypes -from ctypes import c_bool, c_size_t, c_int, c_uint64, c_char_p, pointer, byref import logging import os -from pathlib import Path import sys -from k2hash import OpenFlag, DumpLevel, LogLevel, TimeUnit +from ctypes import byref, c_bool, c_char_p, c_int, c_size_t, c_uint64, pointer +from pathlib import Path + import k2hash +from k2hash import DumpLevel, LogLevel, OpenFlag, TimeUnit LOG = logging.getLogger(__name__) class K2hashIterator: - """ implements iterator of k2hash""" + """implements iterator of k2hash""" + def __init__(self, k2h, key=None): - """Provides constructor """ + """Provides constructor""" if not isinstance(k2h, K2hash): raise TypeError("k2h should be a K2hash object") self._k2h_handle = k2h.handle @@ -43,7 +45,8 @@ def __init__(self, k2h, key=None): if self._key: handle = self._libk2hash.k2h_find_first_str_subkey( - self._k2h_handle, (c_char_p(self._key.encode()))) + self._k2h_handle, (c_char_p(self._key.encode())) + ) else: handle = self._libk2hash.k2h_find_first(self._k2h_handle) @@ -52,11 +55,11 @@ def __init__(self, k2h, key=None): self._handle = handle def __iter__(self): - """Implements iter() itrator interface """ + """Implements iter() itrator interface""" return self def __next__(self): - """Implements next() itrator interface """ + """Implements next() itrator interface""" ppkey = pointer(c_char_p("".encode())) pkeylength = pointer(c_size_t(0)) res = self._libk2hash.k2h_find_get_key(self._handle, ppkey, pkeylength) @@ -70,10 +73,11 @@ def __next__(self): raise StopIteration -class K2hash: +class K2hash: # noqa: pylint: disable=too-many-instance-attributes,too-many-public-methods """ K2hash class provides methods to handle key/value pairs in k2hash hash database. """ + K2H_INVALID_HANDLE = 0 def get_iterator(self, key=None): @@ -87,67 +91,83 @@ def _set_k2h_handle(self): if self._k2hfile == "": if self._flag is None: # case1. no k2hfile + no flag ---> k2hash_open_memory - handle = self._libk2hash.k2h_open_mem(self._maskbit, - self._cmaskbit, - self._maxelementcnt, - self._pagesize) + handle = self._libk2hash.k2h_open_mem( + self._maskbit, self._cmaskbit, self._maxelementcnt, self._pagesize + ) elif self._flag == OpenFlag.MEMORY: # case2. no k2hfile + flag. k2hash_open_memory if flag is memory, otherwise error. - handle = self._libk2hash.k2h_open_mem(self._maskbit, - self._cmaskbit, - self._maxelementcnt, - self._pagesize) + handle = self._libk2hash.k2h_open_mem( + self._maskbit, self._cmaskbit, self._maxelementcnt, self._pagesize + ) else: raise ValueError("flag should be memory if k2hfile is empty") else: if self._flag is None: # case3. k2hfile + no flag ---> k2h_open handle = self._libk2hash.k2h_open( - c_char_p(self._k2hfile.encode()), self._readonly, - self._removefile, self._fullmap, self._maskbit, - self._cmaskbit, self._maxelementcnt, self._pagesize) + c_char_p(self._k2hfile.encode()), + self._readonly, + self._removefile, + self._fullmap, + self._maskbit, + self._cmaskbit, + self._maxelementcnt, + self._pagesize, + ) elif self._flag == OpenFlag.EDIT: # case4. k2hfile + flag ---> k2hash_open_{rw|r|tempfile} if flag is not memory, # Otherwise error handle = self._libk2hash.k2h_open_rw( - c_char_p(self._k2hfile.encode()), self._fullmap, - self._maskbit, self._cmaskbit, self._maxelementcnt, - self._pagesize) + c_char_p(self._k2hfile.encode()), + self._fullmap, + self._maskbit, + self._cmaskbit, + self._maxelementcnt, + self._pagesize, + ) elif self._flag == OpenFlag.READ: # Checks if self._k2hfile exists. if os.path.exists(Path(self._k2hfile)) is True: handle = self._libk2hash.k2h_open_ro( - c_char_p(self._k2hfile.encode()), self._fullmap, - self._maskbit, self._cmaskbit, self._maxelementcnt, - self._pagesize) + c_char_p(self._k2hfile.encode()), + self._fullmap, + self._maskbit, + self._cmaskbit, + self._maxelementcnt, + self._pagesize, + ) else: - raise RuntimeError("{} should exist".format(self._k2hfile)) + raise RuntimeError(f"{self._k2hfile} should exist") elif self._flag == OpenFlag.TEMPFILE: handle = self._libk2hash.k2h_open_tempfile( - c_char_p(self._k2hfile.encode()), self._fullmap, - self._maskbit, self._cmaskbit, self._maxelementcnt, - self._pagesize) + c_char_p(self._k2hfile.encode()), + self._fullmap, + self._maskbit, + self._cmaskbit, + self._maxelementcnt, + self._pagesize, + ) else: - raise ValueError( - "k2hfile should be empty if using flag is memory") + raise ValueError("k2hfile should be empty if using flag is memory") if handle == self.__class__.K2H_INVALID_HANDLE: raise RuntimeError("handle should not be K2H_INVALID_HANDLE") self._handle = handle - def __init__( # noqa: pylint: disable=too-many-branches - self, - k2hfile="", - flag=None, - readonly=True, - removefile=True, - fullmap=True, - maskbit=8, - cmaskbit=4, - maxelementcnt=1024, - pagesize=512, - waitms=0, - logfile=""): + def __init__( # noqa: pylint: disable=too-many-branches,too-many-arguments + self, + k2hfile="", + flag=None, + readonly=True, + removefile=True, + fullmap=True, + maskbit=8, + cmaskbit=4, + maxelementcnt=1024, + pagesize=512, + waitms=0, + logfile="", + ): """ Initialize a new K2hash instnace. """ @@ -196,7 +216,7 @@ def __init__( # noqa: pylint: disable=too-many-branches self._libc = k2hash.get_library_handle()["c"] self._libk2hash = k2hash.get_library_handle()["k2hash"] if not self._libk2hash: - raise Exception('unable to load k2hash library') + raise RuntimeError("unable to load k2hash library") except: LOG.error("Unexpected error:{%s}", sys.exc_info()[0]) raise @@ -205,21 +225,18 @@ def __init__( # noqa: pylint: disable=too-many-branches @property def libk2hash(self): - """returns libk2hash handle """ + """returns libk2hash handle""" return self._libk2hash @property def libc(self): - """returns libc handle """ + """returns libc handle""" return self._libc - def set(self, - key, - val, - password=None, - expire_duration=None, - time_unit=TimeUnit.SECONDS): - """Sets a key/value pair """ + def set( # noqa: pylint: disable=too-many-arguments + self, key, val, password=None, expire_duration=None, time_unit=TimeUnit.SECONDS + ): + """Sets a key/value pair""" if not isinstance(key, str): raise TypeError("key should currently be a str object") if not key: @@ -238,15 +255,18 @@ def set(self, raise TypeError("time_unit should be a TimeUnit object") res = self._libk2hash.k2h_set_str_value_wa( - self._handle, c_char_p(key.encode()), c_char_p(val.encode()), + self._handle, + c_char_p(key.encode()), + c_char_p(val.encode()), (c_char_p(password.encode()) if password else None), - (pointer(c_uint64(expire_duration)) if expire_duration else None)) + (pointer(c_uint64(expire_duration)) if expire_duration else None), + ) LOG.debug("ret:%s", res) return res def get(self, key, password=None): - """Gets the value """ + """Gets the value""" if not isinstance(key, str): raise TypeError("key should currently be a str object") if not key: @@ -257,8 +277,10 @@ def get(self, key, password=None): raise ValueError("password should not be empty") val = self._libk2hash.k2h_get_str_direct_value_wp( - self._handle, c_char_p(key.encode()), - (c_char_p(password.encode()) if password else None)) + self._handle, + c_char_p(key.encode()), + (c_char_p(password.encode()) if password else None), + ) if val: return val.decode() @@ -269,13 +291,14 @@ def add_attribute_plugin_lib(self, path): if not isinstance(path, str): raise TypeError("path should currently be a str object") if not os.path.exists(Path(path)): - raise RuntimeError("path {} should exists".format(path)) + raise RuntimeError(f"path {path} should exists") res = self._libk2hash.k2h_add_attr_plugin_library( - self._handle, c_char_p(path.encode())) + self._handle, c_char_p(path.encode()) + ) if not res: - LOG.error('error in k2h_add_attr_plugin_library') + LOG.error("error in k2h_add_attr_plugin_library") return res def add_decryption_password(self, password): @@ -286,19 +309,22 @@ def add_decryption_password(self, password): raise ValueError("password should not be empty") res = self._libk2hash.k2h_add_attr_crypt_pass( - self._handle, c_char_p(password.encode()), False) + self._handle, c_char_p(password.encode()), False + ) if not res: - LOG.error('error in k2h_add_attr_crypt_pass') + LOG.error("error in k2h_add_attr_crypt_pass") return res - def add_subkey(self, - key, - subkey, - subval, - password=None, - expire_duration=None, - time_unit=TimeUnit.SECONDS): + def add_subkey( # noqa: pylint: disable=too-many-arguments + self, + key, + subkey, + subval, + password=None, + expire_duration=None, + time_unit=TimeUnit.SECONDS, + ): """Adds subkeys to a key/value pair.""" if not isinstance(key, str): raise TypeError("key should currently be a str object") @@ -334,12 +360,11 @@ def add_subkey(self, ) if not res: - LOG.error('error in k2h_add_str_subkey_wa') + LOG.error("error in k2h_add_str_subkey_wa") return res def begin_tx(self, txfile, prefix=None, param=None, expire_duration=None): - """Starts a transaction logging. - """ + """Starts a transaction logging.""" if not isinstance(txfile, str): raise TypeError("txfile should be a str object") if txfile == "": @@ -358,34 +383,32 @@ def begin_tx(self, txfile, prefix=None, param=None, expire_duration=None): raise ValueError("expire_duration should not be positive") res = self._libk2hash.k2h_transaction_param_we( - self._handle, True, c_char_p(txfile.encode()), + self._handle, + True, + c_char_p(txfile.encode()), (c_char_p(prefix.encode()) if prefix else None), (c_size_t(len(prefix)) if prefix else 0), (c_char_p(param.encode()) if param else None), (c_size_t(len(param)) if param else 0), - (pointer(c_uint64(expire_duration)) if expire_duration else None)) + (pointer(c_uint64(expire_duration)) if expire_duration else None), + ) if not res: - LOG.error('error in k2h_transaction_param_we') + LOG.error("error in k2h_transaction_param_we") return res def close(self): - """Closes a k2h file. - """ + """Closes a k2h file.""" res = self._libk2hash.k2h_close_wait(self._handle, self._waitms) if not res: - LOG.error('error in k2h_close_wait') + LOG.error("error in k2h_close_wait") return res @staticmethod def create( # noqa: pylint: disable=too-many-branches - pathname, - maskbit=8, - cmaskbit=4, - maxelementcnt=1024, - pagesize=512): - """Creates a k2hash file. - """ + pathname, maskbit=8, cmaskbit=4, maxelementcnt=1024, pagesize=512 + ): + """Creates a k2hash file.""" if not isinstance(pathname, str): raise TypeError("pathname should be a str object") if not pathname: @@ -411,21 +434,24 @@ def create( # noqa: pylint: disable=too-many-branches # https://docs.python.org/3/library/ctypes.html#ctypes.LibraryLoader.LoadLibrary libk2hash = k2hash.get_library_handle()["k2hash"] if not libk2hash: - raise Exception('unable to load k2hash library') + raise RuntimeError("unable to load k2hash library") except: LOG.error("Unexpected error:{%s}", sys.exc_info()[0]) raise - res = libk2hash.k2h_create(c_char_p(pathname.encode()), c_int(maskbit), - c_int(cmaskbit), c_int(maxelementcnt), - c_size_t(pagesize)) + res = libk2hash.k2h_create( + c_char_p(pathname.encode()), + c_int(maskbit), + c_int(cmaskbit), + c_int(maxelementcnt), + c_size_t(pagesize), + ) if not res: LOG.error("error in k2h_create") return res def dump_to_file(self, path, is_skip_error=True): - """Dumps data to a file. - """ + """Dumps data to a file.""" if not isinstance(path, str): raise TypeError("path should be a str object") if not path: @@ -433,64 +459,65 @@ def dump_to_file(self, path, is_skip_error=True): if not isinstance(is_skip_error, bool): raise TypeError("is_skip_error should be a boolean object") - res = self._libk2hash.k2h_put_archive(self._handle, - c_char_p(path.encode()), - is_skip_error) + res = self._libk2hash.k2h_put_archive( + self._handle, c_char_p(path.encode()), is_skip_error + ) if not res: LOG.error("error in k2h_create") return res def enable_encryption(self, enable=True): - """Enables a feature to encrypt a value. - """ + """Enables a feature to encrypt a value.""" if not isinstance(enable, bool): raise TypeError("enable should be a bool object") - res = self._libk2hash.k2h_set_common_attr(self._handle, None, - c_bool(enable), None, None, - None) + res = self._libk2hash.k2h_set_common_attr( + self._handle, None, c_bool(enable), None, None, None + ) if not res: LOG.error("error in k2h_set_common_attr") return res def enable_history(self, enable=True): - """Enables a feature to record a key modification history. - """ + """Enables a feature to record a key modification history.""" if not isinstance(enable, bool): raise TypeError("enable should be a bool object") - res = self._libk2hash.k2h_set_common_attr(self._handle, None, None, - None, c_bool(enable), None) + res = self._libk2hash.k2h_set_common_attr( + self._handle, None, None, None, c_bool(enable), None + ) if not res: LOG.error("error in k2h_set_common_attr") return res def enable_mtime(self, enable=True): - """Enables a feature to record value modification time. - """ + """Enables a feature to record value modification time.""" if not isinstance(enable, bool): raise TypeError("enable should be a bool object") - res = self._libk2hash.k2h_set_common_attr(self._handle, c_bool(enable), - None, None, None, None) + res = self._libk2hash.k2h_set_common_attr( + self._handle, c_bool(enable), None, None, None, None + ) if not res: LOG.error("error in k2h_set_common_attr") return res def get_attributes(self, key, use_str=True): - """Gets attributes of a key. - """ + """Gets attributes of a key.""" if not isinstance(key, str): raise TypeError("key should currently be a str object") if not key: raise ValueError("key should not be empty") pattrspckcnt = c_int() - res = self._libk2hash.k2h_get_direct_attrs(self._handle, - c_char_p(key.encode()), - c_size_t(len(key)), - byref(pattrspckcnt)) - LOG.debug("type(res):{%s} pattrspckcnt.value{%s}", type(res), - pattrspckcnt.value) + res = self._libk2hash.k2h_get_direct_attrs( + self._handle, + c_char_p(key.encode()), + c_size_t(len(key)), + byref(pattrspckcnt), + ) + LOG.debug( + "type(res):{%s} pattrspckcnt.value{%s}", type(res), pattrspckcnt.value + ) attrs = {} for i in range(pattrspckcnt.value): key_buf = ctypes.create_string_buffer(res[i].keylength) @@ -507,23 +534,23 @@ def get_attributes(self, key, use_str=True): @property def handle(self): - """Returns a Queue handle. - """ + """Returns a Queue handle.""" return self._handle def get_subkeys(self, key, use_str=True): - """ Gets keys of subkeys of a key. - """ + """Gets keys of subkeys of a key.""" if not isinstance(key, str): raise TypeError("key should currently be a str object") if not key: raise ValueError("key should not be empty") pskeypckcnt = c_int() - res = self._libk2hash.k2h_get_direct_subkeys(self._handle, - c_char_p(key.encode()), - c_size_t(len(key) + 1), - byref(pskeypckcnt)) + res = self._libk2hash.k2h_get_direct_subkeys( + self._handle, + c_char_p(key.encode()), + c_size_t(len(key) + 1), + byref(pskeypckcnt), + ) LOG.debug("%s", pskeypckcnt.value) subkeys = [] for i in range(pskeypckcnt.value): @@ -537,20 +564,18 @@ def get_subkeys(self, key, use_str=True): return subkeys def get_tx_file_fd(self): - """Gets a transaction log file descriptor. - """ + """Gets a transaction log file descriptor.""" res = self._libk2hash.k2h_get_transaction_archive_fd(self._handle) return res @staticmethod def get_tx_pool_size(): - """Gets the number of transaction thread pool. - """ + """Gets the number of transaction thread pool.""" try: # https://docs.python.org/3/library/ctypes.html#ctypes.LibraryLoader.LoadLibrary libk2hash = k2hash.get_library_handle()["k2hash"] if not libk2hash: - raise Exception('unable to load k2hash library') + raise RuntimeError("unable to load k2hash library") except: LOG.error("Unexpected error:{%s}", sys.exc_info()[0]) @@ -560,43 +585,38 @@ def get_tx_pool_size(): return res def load_from_file(self, path, is_skip_error=True): - """Loads data from a file. - """ + """Loads data from a file.""" if not isinstance(path, str): raise TypeError("path should currently be a str object") if not os.path.exists(Path(path)): - raise RuntimeError("path{} should exists".format(path)) + raise RuntimeError(f"path{path} should exists") if not isinstance(is_skip_error, bool): raise TypeError("is_skip_error should be a boolean object") - res = self._libk2hash.k2h_load_archive(self._handle, - c_char_p(path.encode()), - c_bool(is_skip_error)) + res = self._libk2hash.k2h_load_archive( + self._handle, c_char_p(path.encode()), c_bool(is_skip_error) + ) if not res: LOG.error("error in k2h_load_archive") return res def print_attribute_plugins(self): - """Prints attribute plugins to stderr. - """ + """Prints attribute plugins to stderr.""" res = self._libk2hash.k2h_print_attr_version(self._handle, None) return res def print_attributes(self): - """Prints attributes to stderr. - """ + """Prints attributes to stderr.""" res = self._libk2hash.k2h_print_attr_information(self._handle, None) return res def print_data_stats(self): - """Prints data statistics. - """ + """Prints data statistics.""" res = self._libk2hash.k2h_print_state(self._handle, None) return res def print_table_stats(self, level=DumpLevel.HEADER): - """Prints k2hash key table information. - """ + """Prints k2hash key table information.""" if not isinstance(level, DumpLevel): raise TypeError("level should be a DumpLevel object") @@ -617,8 +637,7 @@ def print_table_stats(self, level=DumpLevel.HEADER): return res def remove(self, key, remove_all_subkeys=False): - """Removes a key. - """ + """Removes a key.""" if not isinstance(key, str): raise TypeError("key should be a str object") if not key: @@ -626,16 +645,15 @@ def remove(self, key, remove_all_subkeys=False): if not isinstance(remove_all_subkeys, bool): raise TypeError("remove_all_subkeys should be a boolean object") if remove_all_subkeys: - res = self._libk2hash.k2h_remove_str_all(self._handle, - c_char_p(key.encode())) + res = self._libk2hash.k2h_remove_str_all( + self._handle, c_char_p(key.encode()) + ) else: - res = self._libk2hash.k2h_remove_str(self._handle, - c_char_p(key.encode())) + res = self._libk2hash.k2h_remove_str(self._handle, c_char_p(key.encode())) return res def remove_subkeys(self, key, subkeys): - """Removes subkeys from the key. - """ + """Removes subkeys from the key.""" if not isinstance(key, str): raise TypeError("key should be a str object") if not key: @@ -652,15 +670,14 @@ def remove_subkeys(self, key, subkeys): LOG.warning("subkey should not be empty") continue res = self._libk2hash.k2h_remove_str_subkey( - self._handle, c_char_p(key.encode()), - c_char_p(subkey.encode())) + self._handle, c_char_p(key.encode()), c_char_p(subkey.encode()) + ) if not res: return False return True def rename(self, key, newkey): - """Renames a key with a new key. - """ + """Renames a key with a new key.""" if not isinstance(key, str): raise TypeError("key should be a str object") if not key: @@ -669,14 +686,13 @@ def rename(self, key, newkey): raise TypeError("newkey should be a str object") if not newkey: raise ValueError("newkey should not be empty") - res = self._libk2hash.k2h_rename_str(self._handle, - c_char_p(key.encode()), - c_char_p(newkey.encode())) + res = self._libk2hash.k2h_rename_str( + self._handle, c_char_p(key.encode()), c_char_p(newkey.encode()) + ) return res def set_attribute(self, key, attr_name, attr_val): - """Sets an attribute of a key. - """ + """Sets an attribute of a key.""" if not isinstance(key, str): raise TypeError("key should be a str object") if not key: @@ -689,45 +705,43 @@ def set_attribute(self, key, attr_name, attr_val): raise TypeError("attr_val should be a str object") if not attr_val: raise ValueError("attr_val should not be empty") - res = self._libk2hash.k2h_add_attr(self._handle, - c_char_p(key.encode()), - c_size_t(len(key)), - c_char_p(attr_name.encode()), - c_size_t(len(attr_name) + 1), - c_char_p(attr_val.encode()), - c_size_t(len(attr_val) + 1)) + res = self._libk2hash.k2h_add_attr( + self._handle, + c_char_p(key.encode()), + c_size_t(len(key)), + c_char_p(attr_name.encode()), + c_size_t(len(attr_name) + 1), + c_char_p(attr_val.encode()), + c_size_t(len(attr_val) + 1), + ) return res def set_default_encryption_password(self, password): - """Sets the default encryption passphrase. - """ + """Sets the default encryption passphrase.""" if not isinstance(password, str): raise TypeError("password should be a str object") if not password: raise ValueError("password should not be empty") res = self._libk2hash.k2h_add_attr_crypt_pass( - self._handle, c_char_p(password.encode()), c_bool(True)) + self._handle, c_char_p(password.encode()), c_bool(True) + ) return res def set_encryption_password_file(self, path): - """Sets the data encryption password file. - """ + """Sets the data encryption password file.""" if not isinstance(path, str): raise TypeError("path should currently be a str object") if not os.path.exists(Path(path)): - raise RuntimeError("path{} should exists".format(path)) - res = self._libk2hash.k2h_set_common_attr(self._handle, None, None, - c_char_p(path.encode()), - None, None) + raise RuntimeError(f"path{path} should exists") + res = self._libk2hash.k2h_set_common_attr( + self._handle, None, None, c_char_p(path.encode()), None, None + ) if not res: LOG.error("error in k2h_set_common_attr") return res - def set_expiration_duration(self, - expire_duration, - time_unit=TimeUnit.SECONDS): - """Sets the duration to expire a value. - """ + def set_expiration_duration(self, expire_duration, time_unit=TimeUnit.SECONDS): + """Sets the duration to expire a value.""" if not isinstance(expire_duration, int): raise TypeError("expire_duration should be a int object") if expire_duration <= 0: @@ -735,15 +749,14 @@ def set_expiration_duration(self, if not isinstance(time_unit, TimeUnit): raise TypeError("time_unit should be a TimeUnit object") res = self._libk2hash.k2h_set_common_attr( - self._handle, None, None, None, None, - pointer(c_uint64(expire_duration))) + self._handle, None, None, None, None, pointer(c_uint64(expire_duration)) + ) if not res: LOG.error("error in k2h_set_common_attr") return res def set_log_level(self, level=LogLevel.INFO): - """Creates a k2hash file. - """ + """Creates a k2hash file.""" if not isinstance(level, LogLevel): raise TypeError("level should be a LogLevel object") if not level: @@ -761,17 +774,18 @@ def set_log_level(self, level=LogLevel.INFO): self._libk2hash.k2h_set_debug_level_dump() else: raise ValueError( - "level should be either SILENT, ERROR, WARN, INFO or DEBUG") - - def set_subkeys( # noqa: pylint: disable=too-many-branches - self, - key, - subkeys, - password=None, - expire_duration=None, - time_unit=TimeUnit.SECONDS): - """Sets subkeys. - """ + "level should be either SILENT, ERROR, WARN, INFO or DEBUG" + ) + + def set_subkeys( # noqa: pylint: disable=too-many-branches,too-many-arguments + self, + key, + subkeys, + password=None, + expire_duration=None, + time_unit=TimeUnit.SECONDS, + ): + """Sets subkeys.""" if not isinstance(key, str): raise TypeError("key should be a str object") if not key: @@ -812,8 +826,7 @@ def set_subkeys( # noqa: pylint: disable=too-many-branches c_char_p(subval.encode()), c_size_t(len(subval) + 1), (c_char_p(password.encode()) if password else None), - (pointer(c_uint64(expire_duration)) - if expire_duration else None), + (pointer(c_uint64(expire_duration)) if expire_duration else None), ) if res: LOG.debug("k2h_add_str_subkeys:{%s}", res) @@ -823,8 +836,7 @@ def set_subkeys( # noqa: pylint: disable=too-many-branches @staticmethod def set_tx_pool_size(size): - """Sets the number of transaction thread pool. - """ + """Sets the number of transaction thread pool.""" if not isinstance(size, int): raise TypeError("size should be a int object") if size < 0: @@ -833,7 +845,7 @@ def set_tx_pool_size(size): # https://docs.python.org/3/library/ctypes.html#ctypes.LibraryLoader.LoadLibrary libk2hash = k2hash.get_library_handle()["k2hash"] if not libk2hash: - raise Exception('unable to load k2hash library') + raise RuntimeError("unable to load k2hash library") except: LOG.error("Unexpected error:{%s}", sys.exc_info()[0]) raise @@ -844,51 +856,47 @@ def set_tx_pool_size(size): return res def stop_tx(self): - """Stops a transaction logging. - """ - res = self._libk2hash.k2h_disable_transaction(self._handle, - c_bool(False)) + """Stops a transaction logging.""" + res = self._libk2hash.k2h_disable_transaction(self._handle) if not res: - LOG.error('error in k2h_disable_transaction') + LOG.error("error in k2h_disable_transaction") return res def __repr__(self): - """Returns full of members as a string. - """ + """Returns full of members as a string.""" attrs = [] for attr in [ - '_k2hfile', - '_flag', - '_readonly', - '_removefile', - '_fullmap', - '_maskbit', - '_cmaskbit', - '_maxelementcnt', - '_pagesize', - '_waitms', - '_logfile', - '_libc', - '_libk2hash', - '_handle', + "_k2hfile", + "_flag", + "_readonly", + "_removefile", + "_fullmap", + "_maskbit", + "_cmaskbit", + "_maxelementcnt", + "_pagesize", + "_waitms", + "_logfile", + "_libc", + "_libk2hash", + "_handle", ]: # should be hardcoded. val = getattr(self, attr) if val: attrs.append((attr, repr(val))) else: - attrs.append((attr, '')) - values = ', '.join(['%s=%s' % i for i in attrs]) - return '<_K2hash ' + values + '>' + attrs.append((attr, "")) + values = ", ".join(["%s=%s" % i for i in attrs]) # noqa: pylint: disable=consider-using-f-string + return "<_K2hash " + values + ">" @staticmethod def version(): - """Prints version information. - """ + """Prints version information.""" try: # https://docs.python.org/3/library/ctypes.html#ctypes.LibraryLoader.LoadLibrary libk2hash = k2hash.get_library_handle()["k2hash"] if not libk2hash: - raise Exception('unable to load k2hash library') + raise RuntimeError("unable to load k2hash library") except: LOG.error("Unexpected error:{%s}", sys.exc_info()[0]) raise diff --git a/src/k2hash/keyqueue.py b/src/k2hash/keyqueue.py index 8dd5f20..4e62f84 100644 --- a/src/k2hash/keyqueue.py +++ b/src/k2hash/keyqueue.py @@ -15,57 +15,37 @@ from __future__ import absolute_import import logging -from ctypes import c_int, c_uint64, c_char_p, pointer -from k2hash import K2hash +from ctypes import c_char_p, c_int, c_uint64, pointer + +from k2hash import K2hash, BaseQueue LOG = logging.getLogger(__name__) -class KeyQueue: +class KeyQueue(BaseQueue): # noqa: pylint: disable=too-many-instance-attributes """ KeyQueue class provides methods to handle key/value pairs in k2hash hash database. """ - def __init__(self, - k2h, - fifo=True, - prefix=None, - password=None, - expire_duration=None): + + def __init__( # noqa: pylint: disable=too-many-arguments + self, k2h, fifo=True, prefix=None, password=None, expire_duration=None + ): """ Initialize a new KeyQueue instnace. """ - if not isinstance(k2h, K2hash): - raise TypeError("k2h should be a K2hash object") - self._k2h_handle = k2h.handle - self._libc = k2h.libc - self._libk2hash = k2h.libk2hash - - if fifo and not isinstance(fifo, bool): - raise TypeError("fifo should be a boolean object") - self._fifo = fifo - if prefix and not isinstance(prefix, str): - raise TypeError("prefix should be a string object") - self._prefix = prefix - if password and not isinstance(password, str): - raise TypeError("password should be a string object") - self._password = password - if expire_duration and not isinstance(expire_duration, int): - raise TypeError("expire_duration should be a boolean object") - if expire_duration and expire_duration <= 0: - raise ValueError("expire_duration should not be positive") - self._expire_duration = expire_duration - + super().__init__(k2h, fifo=True, prefix=None, password=None, expire_duration=None) handle = self._libk2hash.k2h_keyq_handle_str_prefix( - self._k2h_handle, self._fifo, - (c_char_p(self._prefix.encode()) if self._prefix else None)) + self._k2h_handle, + self._fifo, + (c_char_p(self._prefix.encode()) if self._prefix else None), + ) if handle == K2hash.K2H_INVALID_HANDLE: raise RuntimeError("handle should not be K2H_INVALID_HANDLE") self._handle = handle def put(self, obj): - """Inserts an element into the tail of this queue. - """ + """Inserts an element into the tail of this queue.""" if obj and not isinstance(obj, dict): raise TypeError("obj should be a dict object") if len(obj) == 0: @@ -78,10 +58,16 @@ def put(self, obj): for key, val in obj.items(): res = self._libk2hash.k2h_keyq_str_push_keyval_wa( - self._handle, c_char_p(key.encode()), c_char_p(val.encode()), - (c_char_p(self._password.encode()) if self._password else - None), (pointer(c_uint64(self._expire_duration)) - if self._expire_duration else None)) + self._handle, + c_char_p(key.encode()), + c_char_p(val.encode()), + (c_char_p(self._password.encode()) if self._password else None), + ( + pointer(c_uint64(self._expire_duration)) + if self._expire_duration + else None + ), + ) if res: LOG.debug("q_push:{%s}", res) else: @@ -89,8 +75,7 @@ def put(self, obj): return True def clear(self): - """Removes all of the elements from this collection (optional operation). - """ + """Removes all of the elements from this collection (optional operation).""" count = self.qsize() if count > 0: res = self._libk2hash.k2h_keyq_remove(self._handle, c_int(count)) @@ -105,14 +90,12 @@ def close(self): return res def qsize(self): - """Returns the number of queue. - """ + """Returns the number of queue.""" res = self._libk2hash.k2h_keyq_count(self._handle) return res def element(self, position=0): - """Finds and gets a object from the head of this queue. - """ + """Finds and gets a object from the head of this queue.""" if not isinstance(position, int): raise TypeError("position should be a int object") if position < 0: @@ -121,8 +104,12 @@ def element(self, position=0): ppkey = pointer(c_char_p("".encode())) ppval = pointer(c_char_p("".encode())) res = self._libk2hash.k2h_keyq_str_read_keyval_wp( - self._handle, ppkey, ppval, c_int(position), - (c_char_p(self._password.encode()) if self._password else None)) + self._handle, + ppkey, + ppval, + c_int(position), + (c_char_p(self._password.encode()) if self._password else None), + ) res = {} if ppkey.contents.value: pkey = ppkey.contents.value.decode() @@ -134,26 +121,21 @@ def element(self, position=0): res[pkey] = pval return res - @property - def handle(self): - """Returns a Queue handle. - """ - return self._handle - def empty(self): - """Returns true if, and only if, queue size is 0. - """ + """Returns true if, and only if, queue size is 0.""" res = self._libk2hash.k2h_keyq_empty(self._handle) return res def get(self): - """Finds and gets a object from the head of this queue. - """ + """Finds and gets a object from the head of this queue.""" ppkey = pointer(c_char_p("".encode())) ppval = pointer(c_char_p("".encode())) res = self._libk2hash.k2h_keyq_str_pop_keyval_wp( - self._handle, ppkey, ppval, - (c_char_p(self._password.encode()) if self._password else None)) + self._handle, + ppkey, + ppval, + (c_char_p(self._password.encode()) if self._password else None), + ) res = {} if ppkey.contents.value: pkey = ppkey.contents.value.decode() @@ -166,14 +148,12 @@ def get(self): return res def print(self): - """Print the objects in this queue. - """ + """Print the objects in this queue.""" res = self._libk2hash.k2h_keyq_dump(self._handle, None) return res def remove(self, count=1): - """Removes objects from this queue. - """ + """Removes objects from this queue.""" if not isinstance(count, int): raise TypeError("count should be a int object") if count <= 0: @@ -186,22 +166,6 @@ def remove(self, count=1): vals.append(val) return vals - def __repr__(self): - """Returns full of members as a string. - """ - attrs = [] - for attr in [ - '_handle', '_k2h_handle', '_libk2hash', '_fifo', '_prefix', - '_expire_duration' - ]: # should be hardcoded. - val = getattr(self, attr) - if val: - attrs.append((attr, repr(val))) - else: - attrs.append((attr, '')) - values = ', '.join(['%s=%s' % i for i in attrs]) - return '<_K2hQueue ' + values + '>' - # # Local variables: diff --git a/src/k2hash/queue.py b/src/k2hash/queue.py index 498d040..b8d21d3 100644 --- a/src/k2hash/queue.py +++ b/src/k2hash/queue.py @@ -14,59 +14,41 @@ """K2hash Python Driver under MIT License""" from __future__ import absolute_import -import logging -from ctypes import c_ubyte, c_size_t, c_int, c_uint64, c_char_p, pointer, POINTER, cast import copy -from k2hash import K2hash, AttrPack +import logging +from ctypes import (POINTER, c_char_p, c_int, c_size_t, c_ubyte, c_uint64, + cast, pointer) + +from k2hash import AttrPack, K2hash, BaseQueue LOG = logging.getLogger(__name__) -class Queue: +class Queue(BaseQueue): # noqa: pylint:disable=too-many-instance-attributes """ Queue class provides methods to handle key/value pairs in k2hash hash database. """ - def __init__(self, - k2h, - fifo=True, - prefix=None, - password=None, - expire_duration=None): + + def __init__( # noqa: pylint: disable=too-many-arguments + self, k2h, fifo=True, prefix=None, password=None, expire_duration=None + ): """ Initialize a new Queue instnace. """ - if not isinstance(k2h, K2hash): - raise TypeError("k2h should be a K2hash object") - self._k2h_handle = k2h.handle - self._libc = k2h.libc - self._libk2hash = k2h.libk2hash - - if fifo and not isinstance(fifo, bool): - raise TypeError("fifo should be a boolean object") - self._fifo = fifo - if prefix and not isinstance(prefix, str): - raise TypeError("prefix should be a string object") - self._prefix = prefix - if password and not isinstance(password, str): - raise TypeError("password should be a string object") - self._password = password - if expire_duration and not isinstance(expire_duration, int): - raise TypeError("expire_duration should be a boolean object") - if expire_duration and expire_duration <= 0: - raise ValueError("expire_duration should not be positive") - self._expire_duration = expire_duration - + super().__init__(k2h, fifo=True, prefix=None, password=None, expire_duration=None) handle = self._libk2hash.k2h_q_handle_str_prefix( - self._k2h_handle, self._fifo, - (c_char_p(self._prefix.encode()) if self._prefix else None)) + self._k2h_handle, + self._fifo, + (c_char_p(self._prefix.encode()) if self._prefix else None), + ) if handle == K2hash.K2H_INVALID_HANDLE: raise RuntimeError("handle should not be K2H_INVALID_HANDLE") self._handle = handle + def put(self, obj, attrs=None): - """Inserts an element into the tail of this queue. - """ + """Inserts an element into the tail of this queue.""" value = [] if not isinstance(obj, list) and not isinstance(obj, str): raise TypeError("obj should be a str or list object") @@ -97,16 +79,21 @@ def put(self, obj, attrs=None): ap_array_pointer = pointer(ap_array()[0]) res = self._libk2hash.k2h_q_str_push_wa( - self._handle, c_char_p(obj.encode()), - (ap_array_pointer if ap_array else None), 0, + self._handle, + c_char_p(obj.encode()), + (ap_array_pointer if ap_array else None), + 0, (c_char_p(self._password.encode()) if self._password else None), - (pointer(c_uint64(self._expire_duration)) - if self._expire_duration else None)) + ( + pointer(c_uint64(self._expire_duration)) + if self._expire_duration + else None + ), + ) return res def clear(self): - """Removes all of the elements from this collection (optional operation). - """ + """Removes all of the elements from this collection (optional operation).""" count = self.qsize() if count > 0: res = self._libk2hash.k2h_q_remove(self._handle, c_int(count)) @@ -121,24 +108,26 @@ def close(self): return res def qsize(self): - """Returns the number of queue. - """ + """Returns the number of queue.""" res = self._libk2hash.k2h_q_count(self._handle) return res def element(self, position=0): - """Finds and gets a object from the head of this queue. - """ + """Finds and gets a object from the head of this queue.""" if not isinstance(position, int): raise TypeError("position should be a int object") - if (position < 0): + if position < 0: raise ValueError("count should be positive") ppdata = pointer(c_char_p("".encode())) pdatalen = pointer(c_size_t(0)) self._libk2hash.k2h_q_read_wp( - self._handle, ppdata, pdatalen, c_int(position), - (c_char_p(self._password.encode()) if self._password else None)) + self._handle, + ppdata, + pdatalen, + c_int(position), + (c_char_p(self._password.encode()) if self._password else None), + ) datalen = pdatalen.contents.value if datalen > 0: pdata = ppdata.contents.value.decode() @@ -146,25 +135,19 @@ def element(self, position=0): return pdata return "" - @property - def handle(self): - """Returns a Queue handle. - """ - return self._handle - def empty(self): - """Returns true if, and only if, queue size is 0. - """ + """Returns true if, and only if, queue size is 0.""" res = self._libk2hash.k2h_q_empty(self._handle) return res def get(self): - """Finds and gets a object from the head of this queue. - """ + """Finds and gets a object from the head of this queue.""" ppval = pointer(c_char_p("".encode())) res = self._libk2hash.k2h_q_str_pop_wp( - self._handle, ppval, - (c_char_p(self._password.encode()) if self._password else None)) + self._handle, + ppval, + (c_char_p(self._password.encode()) if self._password else None), + ) if res and ppval.contents.value: pval = ppval.contents.value.decode() @@ -174,14 +157,12 @@ def get(self): return "" def print(self): - """Print the objects in this queue. - """ + """Print the objects in this queue.""" res = self._libk2hash.k2h_q_dump(self._handle, None) return res def remove(self, count=1): - """Removes objects from this queue. - """ + """Removes objects from this queue.""" if not isinstance(count, int): raise TypeError("count should be a int object") if count <= 0: @@ -194,23 +175,6 @@ def remove(self, count=1): vals.append(val) return vals - def __repr__(self): - """Returns full of members as a string. - """ - attrs = [] - for attr in [ - '_handle', '_k2h_handle', '_libk2hash', '_fifo', '_prefix', - '_expire_duration' - ]: # should be hardcoded. - val = getattr(self, attr) - if val: - attrs.append((attr, repr(val))) - else: - attrs.append((attr, '')) - values = ', '.join(['%s=%s' % i for i in attrs]) - return '<_Queue ' + values + '>' - - # # Local variables: # tab-width: 4 diff --git a/src/k2hash/tests/test_k2hash.py b/src/k2hash/tests/test_k2hash.py index 35e0698..c74a7a7 100644 --- a/src/k2hash/tests/test_k2hash.py +++ b/src/k2hash/tests/test_k2hash.py @@ -11,14 +11,15 @@ # CREATE: Tue Feb 08 2022 # REVISION: # -import unittest -import k2hash -import logging import ctypes +import logging import time +import unittest + +import k2hash -class TestK2hashIterator(unittest.TestCase): +class TestK2hashIterator(unittest.TestCase): def test_K2hashIterator_construct(self): db = k2hash.K2hash() self.assertTrue(isinstance(db, k2hash.K2hash)) @@ -46,14 +47,13 @@ def test_K2hashIterator_construct_key(self): self.assertTrue(subkey == next(ki)) db.close() -class TestK2hash(unittest.TestCase): +class TestK2hash(unittest.TestCase): def test_K2hash_construct(self): db = k2hash.K2hash() self.assertTrue(isinstance(db, k2hash.K2hash)) db.close() - def test_K2hash_get_iterator(self): db = k2hash.K2hash() self.assertTrue(isinstance(db, k2hash.K2hash)) @@ -148,7 +148,6 @@ def test_K2hash_create(self): k2h_file = "test.k2h" self.assertTrue(k2hash.K2hash.create(k2h_file), True) - def test_K2hash_dump_to_file(self): db = k2hash.K2hash() self.assertTrue(isinstance(db, k2hash.K2hash)) @@ -281,7 +280,7 @@ def test_K2hash_load_from_file(self): k2h_file = "test.k2h" self.assertTrue(db.dump_to_file(k2h_file), val) db.close() - + db = None db = k2hash.K2hash() self.assertTrue(isinstance(db, k2hash.K2hash)) @@ -392,12 +391,13 @@ def test_K2hash_set_encryption_password_file(self): self.assertTrue(db.set(key, val), True) self.assertTrue(db.get(key), val) # 1. make the password file for test - password_file="password.txt" + password_file = "password.txt" password = "secretstring" import os - with open(password_file, 'w') as f: + + with open(password_file, "w") as f: print("{}".format(password), file=f) - + # 2. call the api self.assertTrue(db.set_encryption_password_file(password_file), True) db.close() @@ -470,7 +470,7 @@ def test_K2hash_version(self): self.assertTrue(k2hash.K2hash.version() == None) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() # diff --git a/src/k2hash/tests/test_k2hash_package.py b/src/k2hash/tests/test_k2hash_package.py index 2444427..e4df1a2 100644 --- a/src/k2hash/tests/test_k2hash_package.py +++ b/src/k2hash/tests/test_k2hash_package.py @@ -11,26 +11,27 @@ # CREATE: Tue Feb 08 2022 # REVISION: # +import logging import unittest + import k2hash -import logging -class TestK2hashPackage(unittest.TestCase): +class TestK2hashPackage(unittest.TestCase): def test_get_library_handle(self): libk2hash = k2hash.get_library_handle() self.assertTrue(libk2hash) self.assertTrue(isinstance(libk2hash, dict)) - self.assertTrue(libk2hash['c']) - self.assertTrue(libk2hash['k2hash']) + self.assertTrue(libk2hash["c"]) + self.assertTrue(libk2hash["k2hash"]) def test_set_log_level(self): k2hash.set_log_level(logging.INFO) - logger = logging.getLogger('k2hash') - self.assertEqual(logging.getLevelName(logger.level), 'INFO') + logger = logging.getLogger("k2hash") + self.assertEqual(logging.getLevelName(logger.level), "INFO") -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() # diff --git a/src/k2hash/tests/test_keyqueue.py b/src/k2hash/tests/test_keyqueue.py index f91373c..a8c2759 100644 --- a/src/k2hash/tests/test_keyqueue.py +++ b/src/k2hash/tests/test_keyqueue.py @@ -11,13 +11,14 @@ # CREATE: Tue Feb 08 2022 # REVISION: # -import unittest -import k2hash import logging import time +import unittest + +import k2hash -class TestKeyQueue(unittest.TestCase): +class TestKeyQueue(unittest.TestCase): def test_KeyQueue_construct(self): db = k2hash.K2hash() q = k2hash.KeyQueue(db) @@ -38,7 +39,7 @@ def test_KeyQueue_put_with_obj(self): db = k2hash.K2hash() q = k2hash.KeyQueue(db) self.assertTrue(isinstance(q, k2hash.KeyQueue)) - obj={"k1": "v1"} + obj = {"k1": "v1"} self.assertTrue(q.put(obj), True) self.assertTrue(q.get(), obj) self.assertTrue(q.close(), True) @@ -48,7 +49,7 @@ def test_KeyQueue_clear(self): db = k2hash.K2hash() q = k2hash.KeyQueue(db) self.assertTrue(isinstance(q, k2hash.KeyQueue)) - obj={"k1": "v1"} + obj = {"k1": "v1"} self.assertTrue(q.put(obj), True) self.assertTrue(q.qsize() == 1) self.assertTrue(q.clear(), True) @@ -67,7 +68,7 @@ def test_KeyQueue_qsize(self): db = k2hash.K2hash() q = k2hash.KeyQueue(db) self.assertTrue(isinstance(q, k2hash.KeyQueue)) - obj={"k1": "v1"} + obj = {"k1": "v1"} self.assertTrue(q.put(obj), True) self.assertTrue(q.qsize() == 1) self.assertTrue(q.close(), True) @@ -76,7 +77,7 @@ def test_KeyQueue_qsize(self): def test_KeyQueue_element(self): db = k2hash.K2hash() q = k2hash.KeyQueue(db) - obj={"k1": "v1"} + obj = {"k1": "v1"} self.assertTrue(q.put(obj), True) self.assertTrue(q.element(), obj) self.assertTrue(q.close(), True) @@ -86,7 +87,7 @@ def test_KeyQueue_element_with_position(self): db = k2hash.K2hash() q = k2hash.KeyQueue(db) self.assertTrue(isinstance(q, k2hash.KeyQueue)) - obj={"k1": "v1"} + obj = {"k1": "v1"} self.assertTrue(q.put(obj), True) self.assertTrue(q.element(1) == {}) self.assertTrue(q.close(), True) @@ -112,7 +113,7 @@ def test_KeyQueue_get(self): db = k2hash.K2hash() q = k2hash.KeyQueue(db) self.assertTrue(isinstance(q, k2hash.KeyQueue)) - obj={"k1": "v1"} + obj = {"k1": "v1"} self.assertTrue(q.put(obj), True) self.assertTrue(q.get(), obj) self.assertTrue(q.close(), True) @@ -130,7 +131,7 @@ def test_KeyQueue_remove(self): db = k2hash.K2hash() q = k2hash.KeyQueue(db) self.assertTrue(isinstance(q, k2hash.KeyQueue)) - obj={"k1": "v1"} + obj = {"k1": "v1"} self.assertTrue(q.put(obj), True) self.assertTrue(q.qsize() == 1) self.assertTrue(q.remove(), True) @@ -142,7 +143,7 @@ def test_KeyQueue_remove_with_count(self): db = k2hash.K2hash() q = k2hash.KeyQueue(db) self.assertTrue(isinstance(q, k2hash.KeyQueue)) - obj={"k1": "v1"} + obj = {"k1": "v1"} self.assertTrue(q.put(obj), True) self.assertTrue(q.qsize() == 1) self.assertTrue(q.remove(1), True) @@ -157,7 +158,8 @@ def test_KeyQueue_repr(self): self.assertTrue(q.close(), True) db.close() -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() # diff --git a/src/k2hash/tests/test_queue.py b/src/k2hash/tests/test_queue.py index a93d0a4..895b10e 100644 --- a/src/k2hash/tests/test_queue.py +++ b/src/k2hash/tests/test_queue.py @@ -11,13 +11,14 @@ # CREATE: Tue Feb 08 2022 # REVISION: # -import unittest -import k2hash import logging import time +import unittest + +import k2hash -class TestQueue(unittest.TestCase): +class TestQueue(unittest.TestCase): def test_Queue_construct(self): db = k2hash.K2hash() q = k2hash.Queue(db) @@ -38,7 +39,7 @@ def test_Queue_put_with_obj(self): db = k2hash.K2hash() q = k2hash.Queue(db) self.assertTrue(isinstance(q, k2hash.Queue)) - obj="v1" + obj = "v1" self.assertTrue(q.put(obj), True) self.assertTrue(q.get(), obj) self.assertTrue(q.close(), True) @@ -48,8 +49,8 @@ def test_Queue_put_with_obj_attrs(self): db = k2hash.K2hash() q = k2hash.Queue(db) self.assertTrue(isinstance(q, k2hash.Queue)) - obj="v1" - attrs={"attrname1": "attrval1"} + obj = "v1" + attrs = {"attrname1": "attrval1"} self.assertTrue(q.put(obj, attrs), True) self.assertTrue(q.get(), obj) self.assertTrue(q.close(), True) @@ -59,7 +60,7 @@ def test_Queue_clear(self): db = k2hash.K2hash() q = k2hash.Queue(db) self.assertTrue(isinstance(q, k2hash.Queue)) - obj="v1" + obj = "v1" self.assertTrue(q.put(obj), True) self.assertTrue(q.qsize() == 1) self.assertTrue(q.clear(), True) @@ -78,7 +79,7 @@ def test_Queue_qsize(self): db = k2hash.K2hash() q = k2hash.Queue(db) self.assertTrue(isinstance(q, k2hash.Queue)) - obj="v1" + obj = "v1" self.assertTrue(q.put(obj), True) self.assertTrue(q.qsize() == 1) self.assertTrue(q.close(), True) @@ -87,7 +88,7 @@ def test_Queue_qsize(self): def test_Queue_element(self): db = k2hash.K2hash() q = k2hash.Queue(db) - obj="v1" + obj = "v1" self.assertTrue(q.put(obj), True) self.assertTrue(q.element(), obj) self.assertTrue(q.close(), True) @@ -97,7 +98,7 @@ def test_Queue_element_with_position(self): db = k2hash.K2hash() q = k2hash.Queue(db) self.assertTrue(isinstance(q, k2hash.Queue)) - obj="v1" + obj = "v1" self.assertTrue(q.put(obj), True) self.assertTrue(q.element(1) == "") self.assertTrue(q.close(), True) @@ -123,7 +124,7 @@ def test_Queue_get(self): db = k2hash.K2hash() q = k2hash.Queue(db) self.assertTrue(isinstance(q, k2hash.Queue)) - obj="v1" + obj = "v1" self.assertTrue(q.put(obj), True) self.assertTrue(q.get(), obj) self.assertTrue(q.close(), True) @@ -141,7 +142,7 @@ def test_Queue_remove(self): db = k2hash.K2hash() q = k2hash.Queue(db) self.assertTrue(isinstance(q, k2hash.Queue)) - obj="v1" + obj = "v1" self.assertTrue(q.put(obj), True) self.assertTrue(q.qsize() == 1) self.assertTrue(q.remove(), True) @@ -153,7 +154,7 @@ def test_Queue_remove_with_count(self): db = k2hash.K2hash() q = k2hash.Queue(db) self.assertTrue(isinstance(q, k2hash.Queue)) - obj="v1" + obj = "v1" self.assertTrue(q.put(obj), True) self.assertTrue(q.qsize() == 1) self.assertTrue(q.remove(1), True) @@ -168,7 +169,8 @@ def test_Queue_repr(self): self.assertTrue(q.close(), True) db.close() -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() #