mirror of
https://github.com/njvack/markdown-to-json.git
synced 2026-04-22 03:02:02 -04:00
5
.devcontainer/Dockerfile
Normal file
5
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
||||
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.245.0/containers/python-3/.devcontainer/base.Dockerfile
|
||||
|
||||
# [Choice] Python version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.10, 3.9, 3.8, 3.7, 3.6, 3-bullseye, 3.10-bullseye, 3.9-bullseye, 3.8-bullseye, 3.7-bullseye, 3.6-bullseye, 3-buster, 3.10-buster, 3.9-buster, 3.8-buster, 3.7-buster, 3.6-buster
|
||||
# FROM mcr.microsoft.com/devcontainers/python:3.11-bullseye
|
||||
FROM ghcr.io/matthewdeanmartin/convenient_py_devcontainer:latest
|
||||
62
.devcontainer/devcontainer.json
Normal file
62
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,62 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
|
||||
// https://github.com/microsoft/vscode-dev-containers/tree/v0.245.0/containers/python-3
|
||||
{
|
||||
"name": "Python 3",
|
||||
"forwardPorts": [8000],
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
"context": ".."
|
||||
},
|
||||
|
||||
// Configure tool-specific properties.
|
||||
"customizations": {
|
||||
// Configure properties specific to VS Code.
|
||||
"vscode": {
|
||||
// Set *default* container specific settings.json values on container create.
|
||||
"settings": {
|
||||
"python.defaultInterpreterPath": "/usr/local/bin/python",
|
||||
"python.linting.enabled": true,
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
|
||||
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
|
||||
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
|
||||
"python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
|
||||
"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
|
||||
"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
|
||||
"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
|
||||
"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
|
||||
"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint",
|
||||
"python.testing.unittestEnabled": false,
|
||||
"python.testing.pytestEnabled": true,
|
||||
"workbench.startupEditor": "newUntitledFile"
|
||||
},
|
||||
|
||||
// Add the IDs of extensions you want installed when the container is created.
|
||||
"extensions": [
|
||||
"ms-python.python",
|
||||
"ms-python.vscode-pylance"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
"postCreateCommand": "pipenv sync --dev",
|
||||
|
||||
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||
"remoteUser": "vscode",
|
||||
// "folders": [
|
||||
// {
|
||||
// "path": ".devcontainer"
|
||||
// }
|
||||
// ],
|
||||
"features": {
|
||||
"ghcr.io/devcontainers-contrib/features/black:2": {},
|
||||
"ghcr.io/devcontainers-contrib/features/coverage-py:2": {},
|
||||
"ghcr.io/devcontainers-contrib/features/isort:2": {},
|
||||
"ghcr.io/devcontainers-contrib/features/pipenv:2": {},
|
||||
"ghcr.io/devcontainers-contrib/features/pylint:2": {}
|
||||
}
|
||||
}
|
||||
54
.github/workflows/build.yml
vendored
Normal file
54
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
name: Build and Test
|
||||
|
||||
on: [ push ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
cache: 'pipenv' # caching pipenv dependencies
|
||||
- name: Install pipenv and pipx
|
||||
run: |
|
||||
pip install pipenv && pip install pipx
|
||||
|
||||
- name: Install Dependencies
|
||||
run: pipenv install --dev --skip-lock
|
||||
- name: Install global dependencies
|
||||
run: |
|
||||
pipx install isort && pipx install black && pipx install bandit && pipx install pylint && \
|
||||
pipx install pre-commit && pipx install poetry
|
||||
- name: Run make
|
||||
run: pipenv run make publish
|
||||
- name: Upload Package
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
name: packages
|
||||
path: dist/
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
|
||||
pypi-publish:
|
||||
name: Upload release to PyPI
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: pypi
|
||||
url: https://pypi.org/p/markdown-to-json
|
||||
permissions:
|
||||
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
||||
# comment out next line if manual approval in place for pypi environment
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
|
||||
steps:
|
||||
- name: Get packages
|
||||
uses: actions/download-artifact@v3.0.2
|
||||
with:
|
||||
name: packages
|
||||
path: dist/
|
||||
- name: Publish package distributions to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
needs: build
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -55,3 +55,7 @@ docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
.idea/
|
||||
|
||||
.build_history/
|
||||
|
||||
36
.pre-commit-config.yaml
Normal file
36
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
hooks:
|
||||
# each hook takes about 1s to run. These are expensive-ish checks
|
||||
- id: check-added-large-files
|
||||
- id: check-yaml
|
||||
- id: check-builtin-literals
|
||||
- id: check-byte-order-marker
|
||||
- id: check-case-conflict
|
||||
- id: check-merge-conflict
|
||||
- id: check-symlinks
|
||||
- id: check-toml
|
||||
- id: debug-statements
|
||||
- id: detect-private-key
|
||||
- id: fix-encoding-pragma
|
||||
args: [ --remove ]
|
||||
- id: forbid-new-submodules
|
||||
- repo: https://github.com/abravalheri/validate-pyproject
|
||||
rev: v0.10.1
|
||||
hooks:
|
||||
- id: validate-pyproject
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.12.0
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.222
|
||||
hooks:
|
||||
- id: ruff
|
||||
exclude: ^markdown_to_json/vendor/.*$
|
||||
args: [
|
||||
"--config",
|
||||
"pyproject.toml",
|
||||
]
|
||||
629
.pylintrc
Normal file
629
.pylintrc
Normal file
@@ -0,0 +1,629 @@
|
||||
[MAIN]
|
||||
|
||||
# Analyse import fallback blocks. This can be used to support both Python 2 and
|
||||
# 3 compatible code, which means that the block might have code that exists
|
||||
# only in one or another interpreter, leading to false positives when analysed.
|
||||
analyse-fallback-blocks=no
|
||||
|
||||
# Load and enable all available extensions. Use --list-extensions to see a list
|
||||
# all available extensions.
|
||||
#enable-all-extensions=
|
||||
|
||||
# In error mode, checkers without error messages are disabled and for others,
|
||||
# only the ERROR messages are displayed, and no reports are done by default.
|
||||
#errors-only=
|
||||
|
||||
# Always return a 0 (non-error) status code, even if lint errors are found.
|
||||
# This is primarily useful in continuous integration scripts.
|
||||
#exit-zero=
|
||||
|
||||
# A comma-separated list of package or module names from where C extensions may
|
||||
# be loaded. Extensions are loading into the active Python interpreter and may
|
||||
# run arbitrary code.
|
||||
extension-pkg-allow-list=
|
||||
|
||||
# A comma-separated list of package or module names from where C extensions may
|
||||
# be loaded. Extensions are loading into the active Python interpreter and may
|
||||
# run arbitrary code. (This is an alternative name to extension-pkg-allow-list
|
||||
# for backward compatibility.)
|
||||
extension-pkg-whitelist=
|
||||
|
||||
# Return non-zero exit code if any of these messages/categories are detected,
|
||||
# even if score is above --fail-under value. Syntax same as enable. Messages
|
||||
# specified are enabled, while categories only check already-enabled messages.
|
||||
fail-on=
|
||||
|
||||
# Specify a score threshold to be exceeded before program exits with error.
|
||||
fail-under=10
|
||||
|
||||
# Interpret the stdin as a python script, whose filename needs to be passed as
|
||||
# the module_or_package argument.
|
||||
#from-stdin=
|
||||
|
||||
# Files or directories to be skipped. They should be base names, not paths.
|
||||
ignore=CVS
|
||||
|
||||
# Add files or directories matching the regex patterns to the ignore-list. The
|
||||
# regex matches against paths and can be in Posix or Windows format.
|
||||
ignore-paths=^markdown_to_json/vendor/.*$
|
||||
|
||||
|
||||
# Files or directories matching the regex patterns are skipped. The regex
|
||||
# matches against base names, not paths. The default value ignores Emacs file
|
||||
# locks
|
||||
ignore-patterns=^\.#
|
||||
|
||||
# List of module names for which member attributes should not be checked
|
||||
# (useful for modules/projects where namespaces are manipulated during runtime
|
||||
# and thus existing member attributes cannot be deduced by static analysis). It
|
||||
# supports qualified module names, as well as Unix pattern matching.
|
||||
ignored-modules=
|
||||
|
||||
# Python code to execute, usually for sys.path manipulation such as
|
||||
# pygtk.require().
|
||||
#init-hook=
|
||||
|
||||
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
|
||||
# number of processors available to use.
|
||||
jobs=1
|
||||
|
||||
# Control the amount of potential inferred values when inferring a single
|
||||
# object. This can help the performance when dealing with large functions or
|
||||
# complex, nested conditions.
|
||||
limit-inference-results=100
|
||||
|
||||
# List of plugins (as comma separated values of python module names) to load,
|
||||
# usually to register additional checkers.
|
||||
load-plugins=
|
||||
|
||||
# Pickle collected data for later comparisons.
|
||||
persistent=yes
|
||||
|
||||
# Minimum Python version to use for version dependent checks. Will default to
|
||||
# the version used to run pylint.
|
||||
py-version=3.10
|
||||
|
||||
# Discover python modules and packages in the file system subtree.
|
||||
recursive=no
|
||||
|
||||
# When enabled, pylint would attempt to guess common misconfiguration and emit
|
||||
# user-friendly hints instead of false-positive error messages.
|
||||
suggestion-mode=yes
|
||||
|
||||
# Allow loading of arbitrary C extensions. Extensions are imported into the
|
||||
# active Python interpreter and may run arbitrary code.
|
||||
unsafe-load-any-extension=no
|
||||
|
||||
# In verbose mode, extra non-checker-related info will be displayed.
|
||||
#verbose=
|
||||
|
||||
|
||||
[REPORTS]
|
||||
|
||||
# Python expression which should return a score less than or equal to 10. You
|
||||
# have access to the variables 'fatal', 'error', 'warning', 'refactor',
|
||||
# 'convention', and 'info' which contain the number of messages in each
|
||||
# category, as well as 'statement' which is the total number of statements
|
||||
# analyzed. This score is used by the global evaluation report (RP0004).
|
||||
evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10))
|
||||
|
||||
# Template used to display messages. This is a python new-style format string
|
||||
# used to format the message information. See doc for all details.
|
||||
msg-template=
|
||||
|
||||
# Set the output format. Available formats are text, parseable, colorized, json
|
||||
# and msvs (visual studio). You can also give a reporter class, e.g.
|
||||
# mypackage.mymodule.MyReporterClass.
|
||||
#output-format=
|
||||
|
||||
# Tells whether to display a full report or only the messages.
|
||||
reports=no
|
||||
|
||||
# Activate the evaluation score.
|
||||
score=yes
|
||||
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
|
||||
# Only show warnings with the listed confidence levels. Leave empty to show
|
||||
# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE,
|
||||
# UNDEFINED.
|
||||
confidence=HIGH,
|
||||
CONTROL_FLOW,
|
||||
INFERENCE,
|
||||
INFERENCE_FAILURE,
|
||||
UNDEFINED
|
||||
|
||||
# Disable the message, report, category or checker with the given id(s). You
|
||||
# can either give multiple identifiers separated by comma (,) or put this
|
||||
# option multiple times (only on the command line, not in the configuration
|
||||
# file where it should appear only once). You can also use "--disable=all" to
|
||||
# disable everything first and then re-enable specific checks. For example, if
|
||||
# you want to run only the similarities checker, you can use "--disable=all
|
||||
# --enable=similarities". If you want to run only the classes checker, but have
|
||||
# no Warning level messages displayed, use "--disable=all --enable=classes
|
||||
# --disable=W".
|
||||
disable=raw-checker-failed,
|
||||
bad-inline-option,
|
||||
locally-disabled,
|
||||
file-ignored,
|
||||
suppressed-message,
|
||||
useless-suppression,
|
||||
deprecated-pragma,
|
||||
use-symbolic-message-instead,
|
||||
|
||||
# can't care
|
||||
fixme,
|
||||
too-many-lines,
|
||||
too-many-branches,
|
||||
import-error,
|
||||
too-many-statements,
|
||||
useless-import-alias,
|
||||
too-many-locals,
|
||||
consider-using-from-import, # pylint is wrong
|
||||
duplicate-code,
|
||||
too-many-arguments,
|
||||
logging-fstring-interpolation,
|
||||
too-many-instance-attributes,
|
||||
too-many-return-statements,
|
||||
unnecessary-direct-lambda-call,
|
||||
too-few-public-methods,
|
||||
logging-not-lazy,
|
||||
line-too-long,
|
||||
|
||||
|
||||
# Enable the message, report, category or checker with the given id(s). You can
|
||||
# either give multiple identifier separated by comma (,) or put this option
|
||||
# multiple time (only on the command line, not in the configuration file where
|
||||
# it should appear only once). See also the "--disable" option for examples.
|
||||
enable=c-extension-no-member
|
||||
|
||||
|
||||
[BASIC]
|
||||
|
||||
# Naming style matching correct argument names.
|
||||
argument-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct argument names. Overrides argument-
|
||||
# naming-style. If left empty, argument names will be checked with the set
|
||||
# naming style.
|
||||
#argument-rgx=
|
||||
|
||||
# Naming style matching correct attribute names.
|
||||
attr-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct attribute names. Overrides attr-naming-
|
||||
# style. If left empty, attribute names will be checked with the set naming
|
||||
# style.
|
||||
#attr-rgx=
|
||||
|
||||
# Bad variable names which should always be refused, separated by a comma.
|
||||
bad-names=foo,
|
||||
bar,
|
||||
baz,
|
||||
toto,
|
||||
tutu,
|
||||
tata
|
||||
|
||||
# Bad variable names regexes, separated by a comma. If names match any regex,
|
||||
# they will always be refused
|
||||
bad-names-rgxs=
|
||||
|
||||
# Naming style matching correct class attribute names.
|
||||
class-attribute-naming-style=any
|
||||
|
||||
# Regular expression matching correct class attribute names. Overrides class-
|
||||
# attribute-naming-style. If left empty, class attribute names will be checked
|
||||
# with the set naming style.
|
||||
#class-attribute-rgx=
|
||||
|
||||
# Naming style matching correct class constant names.
|
||||
class-const-naming-style=UPPER_CASE
|
||||
|
||||
# Regular expression matching correct class constant names. Overrides class-
|
||||
# const-naming-style. If left empty, class constant names will be checked with
|
||||
# the set naming style.
|
||||
#class-const-rgx=
|
||||
|
||||
# Naming style matching correct class names.
|
||||
class-naming-style=PascalCase
|
||||
|
||||
# Regular expression matching correct class names. Overrides class-naming-
|
||||
# style. If left empty, class names will be checked with the set naming style.
|
||||
#class-rgx=
|
||||
|
||||
# Naming style matching correct constant names.
|
||||
const-naming-style=UPPER_CASE
|
||||
|
||||
# Regular expression matching correct constant names. Overrides const-naming-
|
||||
# style. If left empty, constant names will be checked with the set naming
|
||||
# style.
|
||||
#const-rgx=
|
||||
|
||||
# Minimum line length for functions/classes that require docstrings, shorter
|
||||
# ones are exempt.
|
||||
docstring-min-length=-1
|
||||
|
||||
# Naming style matching correct function names.
|
||||
function-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct function names. Overrides function-
|
||||
# naming-style. If left empty, function names will be checked with the set
|
||||
# naming style.
|
||||
#function-rgx=
|
||||
|
||||
# Good variable names which should always be accepted, separated by a comma.
|
||||
good-names=i,
|
||||
j,
|
||||
k,
|
||||
ex,
|
||||
Run,
|
||||
_
|
||||
|
||||
# Good variable names regexes, separated by a comma. If names match any regex,
|
||||
# they will always be accepted
|
||||
good-names-rgxs=
|
||||
|
||||
# Include a hint for the correct naming format with invalid-name.
|
||||
include-naming-hint=no
|
||||
|
||||
# Naming style matching correct inline iteration names.
|
||||
inlinevar-naming-style=any
|
||||
|
||||
# Regular expression matching correct inline iteration names. Overrides
|
||||
# inlinevar-naming-style. If left empty, inline iteration names will be checked
|
||||
# with the set naming style.
|
||||
#inlinevar-rgx=
|
||||
|
||||
# Naming style matching correct method names.
|
||||
method-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct method names. Overrides method-naming-
|
||||
# style. If left empty, method names will be checked with the set naming style.
|
||||
#method-rgx=
|
||||
|
||||
# Naming style matching correct module names.
|
||||
module-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct module names. Overrides module-naming-
|
||||
# style. If left empty, module names will be checked with the set naming style.
|
||||
#module-rgx=
|
||||
|
||||
# Colon-delimited sets of names that determine each other's naming style when
|
||||
# the name regexes allow several styles.
|
||||
name-group=
|
||||
|
||||
# Regular expression which should only match function or class names that do
|
||||
# not require a docstring.
|
||||
no-docstring-rgx=^_
|
||||
|
||||
# List of decorators that produce properties, such as abc.abstractproperty. Add
|
||||
# to this list to register other decorators that produce valid properties.
|
||||
# These decorators are taken in consideration only for invalid-name.
|
||||
property-classes=abc.abstractproperty
|
||||
|
||||
# Regular expression matching correct type variable names. If left empty, type
|
||||
# variable names will be checked with the set naming style.
|
||||
#typevar-rgx=
|
||||
|
||||
# Naming style matching correct variable names.
|
||||
variable-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct variable names. Overrides variable-
|
||||
# naming-style. If left empty, variable names will be checked with the set
|
||||
# naming style.
|
||||
#variable-rgx=
|
||||
|
||||
|
||||
[CLASSES]
|
||||
|
||||
# Warn about protected attribute access inside special methods
|
||||
check-protected-access-in-special-methods=no
|
||||
|
||||
# List of method names used to declare (i.e. assign) instance attributes.
|
||||
defining-attr-methods=__init__,
|
||||
__new__,
|
||||
setUp,
|
||||
__post_init__
|
||||
|
||||
# List of member names, which should be excluded from the protected access
|
||||
# warning.
|
||||
exclude-protected=_asdict,
|
||||
_fields,
|
||||
_replace,
|
||||
_source,
|
||||
_make
|
||||
|
||||
# List of valid names for the first argument in a class method.
|
||||
valid-classmethod-first-arg=cls
|
||||
|
||||
# List of valid names for the first argument in a metaclass class method.
|
||||
valid-metaclass-classmethod-first-arg=cls
|
||||
|
||||
|
||||
[DESIGN]
|
||||
|
||||
# List of regular expressions of class ancestor names to ignore when counting
|
||||
# public methods (see R0903)
|
||||
exclude-too-few-public-methods=
|
||||
|
||||
# List of qualified class names to ignore when counting class parents (see
|
||||
# R0901)
|
||||
ignored-parents=
|
||||
|
||||
# Maximum number of arguments for function / method.
|
||||
max-args=5
|
||||
|
||||
# Maximum number of attributes for a class (see R0902).
|
||||
max-attributes=7
|
||||
|
||||
# Maximum number of boolean expressions in an if statement (see R0916).
|
||||
max-bool-expr=5
|
||||
|
||||
# Maximum number of branch for function / method body.
|
||||
max-branches=12
|
||||
|
||||
# Maximum number of locals for function / method body.
|
||||
max-locals=15
|
||||
|
||||
# Maximum number of parents for a class (see R0901).
|
||||
max-parents=7
|
||||
|
||||
# Maximum number of public methods for a class (see R0904).
|
||||
max-public-methods=20
|
||||
|
||||
# Maximum number of return / yield for function / method body.
|
||||
max-returns=6
|
||||
|
||||
# Maximum number of statements in function / method body.
|
||||
max-statements=50
|
||||
|
||||
# Minimum number of public methods for a class (see R0903).
|
||||
min-public-methods=2
|
||||
|
||||
|
||||
[EXCEPTIONS]
|
||||
|
||||
# Exceptions that will emit a warning when caught.
|
||||
overgeneral-exceptions=builtins.BaseException,
|
||||
builtins.Exception
|
||||
|
||||
|
||||
[FORMAT]
|
||||
|
||||
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
|
||||
expected-line-ending-format=
|
||||
|
||||
# Regexp for a line that is allowed to be longer than the limit.
|
||||
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
|
||||
|
||||
# Number of spaces of indent required inside a hanging or continued line.
|
||||
indent-after-paren=4
|
||||
|
||||
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||
# tab).
|
||||
indent-string=' '
|
||||
|
||||
# Maximum number of characters on a single line.
|
||||
max-line-length=200
|
||||
|
||||
# Maximum number of lines in a module.
|
||||
max-module-lines=1000
|
||||
|
||||
# Allow the body of a class to be on the same line as the declaration if body
|
||||
# contains single statement.
|
||||
single-line-class-stmt=no
|
||||
|
||||
# Allow the body of an if to be on the same line as the test if there is no
|
||||
# else.
|
||||
single-line-if-stmt=no
|
||||
|
||||
|
||||
[IMPORTS]
|
||||
|
||||
# List of modules that can be imported at any level, not just the top level
|
||||
# one.
|
||||
allow-any-import-level=
|
||||
|
||||
# Allow wildcard imports from modules that define __all__.
|
||||
allow-wildcard-with-all=no
|
||||
|
||||
# Deprecated modules which should not be used, separated by a comma.
|
||||
deprecated-modules=
|
||||
|
||||
# Output a graph (.gv or any supported image format) of external dependencies
|
||||
# to the given file (report RP0402 must not be disabled).
|
||||
ext-import-graph=
|
||||
|
||||
# Output a graph (.gv or any supported image format) of all (i.e. internal and
|
||||
# external) dependencies to the given file (report RP0402 must not be
|
||||
# disabled).
|
||||
import-graph=
|
||||
|
||||
# Output a graph (.gv or any supported image format) of internal dependencies
|
||||
# to the given file (report RP0402 must not be disabled).
|
||||
int-import-graph=
|
||||
|
||||
# Force import order to recognize a module as part of the standard
|
||||
# compatibility libraries.
|
||||
known-standard-library=
|
||||
|
||||
# Force import order to recognize a module as part of a third party library.
|
||||
known-third-party=enchant
|
||||
|
||||
# Couples of modules and preferred modules, separated by a comma.
|
||||
preferred-modules=
|
||||
|
||||
|
||||
[LOGGING]
|
||||
|
||||
# The type of string formatting that logging methods do. `old` means using %
|
||||
# formatting, `new` is for `{}` formatting.
|
||||
logging-format-style=old
|
||||
|
||||
# Logging modules to check that the string format arguments are in logging
|
||||
# function parameter format.
|
||||
logging-modules=logging
|
||||
|
||||
|
||||
[MISCELLANEOUS]
|
||||
|
||||
# List of note tags to take in consideration, separated by a comma.
|
||||
notes=FIXME,
|
||||
XXX,
|
||||
TODO
|
||||
|
||||
# Regular expression of note tags to take in consideration.
|
||||
notes-rgx=
|
||||
|
||||
|
||||
[REFACTORING]
|
||||
|
||||
# Maximum number of nested blocks for function / method body
|
||||
max-nested-blocks=5
|
||||
|
||||
# Complete name of functions that never returns. When checking for
|
||||
# inconsistent-return-statements if a never returning function is called then
|
||||
# it will be considered as an explicit return statement and no message will be
|
||||
# printed.
|
||||
never-returning-functions=sys.exit,argparse.parse_error
|
||||
|
||||
|
||||
[SIMILARITIES]
|
||||
|
||||
# Comments are removed from the similarity computation
|
||||
ignore-comments=yes
|
||||
|
||||
# Docstrings are removed from the similarity computation
|
||||
ignore-docstrings=yes
|
||||
|
||||
# Imports are removed from the similarity computation
|
||||
ignore-imports=yes
|
||||
|
||||
# Signatures are removed from the similarity computation
|
||||
ignore-signatures=yes
|
||||
|
||||
# Minimum lines number of a similarity.
|
||||
min-similarity-lines=4
|
||||
|
||||
|
||||
[SPELLING]
|
||||
|
||||
# Limits count of emitted suggestions for spelling mistakes.
|
||||
max-spelling-suggestions=4
|
||||
|
||||
# Spelling dictionary name. Available dictionaries: none. To make it work,
|
||||
# install the 'python-enchant' package.
|
||||
spelling-dict=
|
||||
|
||||
# List of comma separated words that should be considered directives if they
|
||||
# appear at the beginning of a comment and should not be checked.
|
||||
spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
|
||||
|
||||
# List of comma separated words that should not be checked.
|
||||
spelling-ignore-words=
|
||||
|
||||
# A path to a file that contains the private dictionary; one word per line.
|
||||
spelling-private-dict-file=
|
||||
|
||||
# Tells whether to store unknown words to the private dictionary (see the
|
||||
# --spelling-private-dict-file option) instead of raising a message.
|
||||
spelling-store-unknown-words=no
|
||||
|
||||
|
||||
[STRING]
|
||||
|
||||
# This flag controls whether inconsistent-quotes generates a warning when the
|
||||
# character used as a quote delimiter is used inconsistently within a module.
|
||||
check-quote-consistency=no
|
||||
|
||||
# This flag controls whether the implicit-str-concat should generate a warning
|
||||
# on implicit string concatenation in sequences defined over several lines.
|
||||
check-str-concat-over-line-jumps=no
|
||||
|
||||
|
||||
[TYPECHECK]
|
||||
|
||||
# List of decorators that produce context managers, such as
|
||||
# contextlib.contextmanager. Add to this list to register other decorators that
|
||||
# produce valid context managers.
|
||||
contextmanager-decorators=contextlib.contextmanager
|
||||
|
||||
# List of members which are set dynamically and missed by pylint inference
|
||||
# system, and so shouldn't trigger E1101 when accessed. Python regular
|
||||
# expressions are accepted.
|
||||
generated-members=
|
||||
|
||||
# Tells whether to warn about missing members when the owner of the attribute
|
||||
# is inferred to be None.
|
||||
ignore-none=yes
|
||||
|
||||
# This flag controls whether pylint should warn about no-member and similar
|
||||
# checks whenever an opaque object is returned when inferring. The inference
|
||||
# can return multiple potential results while evaluating a Python object, but
|
||||
# some branches might not be evaluated, which results in partial inference. In
|
||||
# that case, it might be useful to still emit no-member and other checks for
|
||||
# the rest of the inferred objects.
|
||||
ignore-on-opaque-inference=yes
|
||||
|
||||
# List of symbolic message names to ignore for Mixin members.
|
||||
ignored-checks-for-mixins=no-member,
|
||||
not-async-context-manager,
|
||||
not-context-manager,
|
||||
attribute-defined-outside-init
|
||||
|
||||
# List of class names for which member attributes should not be checked (useful
|
||||
# for classes with dynamically set attributes). This supports the use of
|
||||
# qualified names.
|
||||
ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace
|
||||
|
||||
# Show a hint with possible names when a member name was not found. The aspect
|
||||
# of finding the hint is based on edit distance.
|
||||
missing-member-hint=yes
|
||||
|
||||
# The minimum edit distance a name should have in order to be considered a
|
||||
# similar match for a missing member name.
|
||||
missing-member-hint-distance=1
|
||||
|
||||
# The total number of similar names that should be taken in consideration when
|
||||
# showing a hint for a missing member.
|
||||
missing-member-max-choices=1
|
||||
|
||||
# Regex pattern to define which classes are considered mixins.
|
||||
mixin-class-rgx=.*[Mm]ixin
|
||||
|
||||
# List of decorators that change the signature of a decorated function.
|
||||
signature-mutators=
|
||||
|
||||
|
||||
[VARIABLES]
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid defining new builtins when possible.
|
||||
additional-builtins=
|
||||
|
||||
# Tells whether unused global variables should be treated as a violation.
|
||||
allow-global-unused-variables=yes
|
||||
|
||||
# List of names allowed to shadow builtins
|
||||
allowed-redefined-builtins=
|
||||
|
||||
# List of strings which can identify a callback function by name. A callback
|
||||
# name must start or end with one of those strings.
|
||||
callbacks=cb_,
|
||||
_cb
|
||||
|
||||
# A regular expression matching the name of dummy variables (i.e. expected to
|
||||
# not be used).
|
||||
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
|
||||
|
||||
# Argument names that match this expression will be ignored. Default to name
|
||||
# with leading underscore.
|
||||
ignored-argument-names=_.*|^ignored_|^unused_
|
||||
|
||||
# Tells whether we should check for unused import in __init__ files.
|
||||
init-import=no
|
||||
|
||||
# List of qualified module names which can have objects that can redefine
|
||||
# builtins.
|
||||
redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
|
||||
96
Makefile
Normal file
96
Makefile
Normal file
@@ -0,0 +1,96 @@
|
||||
# isort . && black . && bandit -r . && pylint && pre-commit run --all-files
|
||||
# Get changed files
|
||||
|
||||
FILES := $(wildcard **/*.py)
|
||||
|
||||
# if you wrap everything in pipenv run, it runs slower.
|
||||
ifeq ($(origin VIRTUAL_ENV),undefined)
|
||||
VENV := pipenv run
|
||||
else
|
||||
VENV :=
|
||||
endif
|
||||
|
||||
Pipfile.lock: Pipfile
|
||||
@echo "Installing dependencies"
|
||||
@pipenv install --dev
|
||||
|
||||
clean-pyc:
|
||||
@echo "Removing compiled files"
|
||||
@find . -name '*.pyc' -exec rm -f {} + || true
|
||||
@find . -name '*.pyo' -exec rm -f {} + || true
|
||||
@find . -name '__pycache__' -exec rm -fr {} + || true
|
||||
|
||||
clean-test:
|
||||
@echo "Removing coverage data"
|
||||
@rm -f .coverage || true
|
||||
@rm -f .coverage.* || true
|
||||
|
||||
clean: clean-pyc clean-test
|
||||
|
||||
# tests can't be expected to pass if dependencies aren't installed.
|
||||
# tests are often slow and linting is fast, so run tests on linted code.
|
||||
test: clean .build_history/pylint .build_history/bandit Pipfile.lock
|
||||
@echo "Running unit tests"
|
||||
# $(VENV) pytest markdown-to-json --doctest-modules
|
||||
$(VENV) python -m unittest discover
|
||||
$(VENV) py.test tests --cov=markdown_to_json --cov-report=html --cov-fail-under 30
|
||||
|
||||
.build_history:
|
||||
@mkdir -p .build_history
|
||||
|
||||
.build_history/isort: .build_history $(FILES)
|
||||
@echo "Formatting imports"
|
||||
$(VENV) isort markdown_to_json
|
||||
@touch .build_history/isort
|
||||
|
||||
.PHONY: isort
|
||||
isort: .build_history/isort
|
||||
|
||||
.build_history/black: .build_history .build_history/isort $(FILES)
|
||||
@echo "Formatting code"
|
||||
$(VENV) black . --exclude .virtualenv --exclude .tox
|
||||
@touch .build_history/black
|
||||
|
||||
.PHONY: black
|
||||
black: .build_history/black
|
||||
|
||||
.build_history/pre-commit: .build_history .build_history/isort .build_history/black
|
||||
@echo "Pre-commit checks"
|
||||
$(VENV) pre-commit run --all-files
|
||||
@touch .build_history/pre-commit
|
||||
|
||||
.PHONY: pre-commit
|
||||
pre-commit: .build_history/pre-commit
|
||||
|
||||
.build_history/bandit: .build_history $(FILES)
|
||||
@echo "Security checks"
|
||||
$(VENV) bandit .
|
||||
@touch .build_history/bandit
|
||||
|
||||
.PHONY: bandit
|
||||
bandit: .build_history/bandit
|
||||
|
||||
.PHONY: pylint
|
||||
.build_history/pylint: .build_history .build_history/isort .build_history/black $(FILES)
|
||||
@echo "Linting with pylint"
|
||||
# TODO lint tests
|
||||
$(VENV) pylint markdown_to_json --fail-under 9
|
||||
@touch .build_history/pylint
|
||||
|
||||
.PHONY: ruff
|
||||
.build_history/ruff: .build_history .build_history/isort .build_history/black $(FILES)
|
||||
@echo "Linting with ruff"
|
||||
$(VENV) ruff markdown_to_json tests
|
||||
@touch .build_history/ruff
|
||||
|
||||
.PHONY: ruff
|
||||
ruff: .build_history/ruff
|
||||
|
||||
# for when using -j (jobs, run in parallel)
|
||||
.NOTPARALLEL: .build_history/isort .build_history/black
|
||||
|
||||
check: test pylint ruff bandit pre-commit
|
||||
|
||||
.PHONY: publish
|
||||
publish: check
|
||||
rm -rf dist && poetry build
|
||||
22
Pipfile
Normal file
22
Pipfile
Normal file
@@ -0,0 +1,22 @@
|
||||
[[source]]
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
|
||||
[dev-packages]
|
||||
pytest-cov = "*"
|
||||
pytest = "*"
|
||||
vermin = "*"
|
||||
tox = "*"
|
||||
isort = "*"
|
||||
bandit = "*"
|
||||
black = "*"
|
||||
pylint = "*"
|
||||
ruff = "*"
|
||||
mypy = "*"
|
||||
pre-commit = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.11"
|
||||
663
Pipfile.lock
generated
Normal file
663
Pipfile.lock
generated
Normal file
@@ -0,0 +1,663 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "d2097cab56840d94c2e41d638376f10553236d262588d341b58f3192acd6913e"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.11"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {},
|
||||
"develop": {
|
||||
"astroid": {
|
||||
"hashes": [
|
||||
"sha256:078e5212f9885fa85fbb0cf0101978a336190aadea6e13305409d099f71b2324",
|
||||
"sha256:1039262575027b441137ab4a62a793a9b43defb42c32d5670f38686207cd780f"
|
||||
],
|
||||
"markers": "python_full_version >= '3.7.2'",
|
||||
"version": "==2.15.5"
|
||||
},
|
||||
"bandit": {
|
||||
"hashes": [
|
||||
"sha256:75665181dc1e0096369112541a056c59d1c5f66f9bb74a8d686c3c362b83f549",
|
||||
"sha256:bdfc739baa03b880c2d15d0431b31c658ffc348e907fe197e54e0389dd59e11e"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.7.5"
|
||||
},
|
||||
"black": {
|
||||
"hashes": [
|
||||
"sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5",
|
||||
"sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915",
|
||||
"sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326",
|
||||
"sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940",
|
||||
"sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b",
|
||||
"sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30",
|
||||
"sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c",
|
||||
"sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c",
|
||||
"sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab",
|
||||
"sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27",
|
||||
"sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2",
|
||||
"sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961",
|
||||
"sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9",
|
||||
"sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb",
|
||||
"sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70",
|
||||
"sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331",
|
||||
"sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2",
|
||||
"sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266",
|
||||
"sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d",
|
||||
"sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6",
|
||||
"sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b",
|
||||
"sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925",
|
||||
"sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8",
|
||||
"sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4",
|
||||
"sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==23.3.0"
|
||||
},
|
||||
"cachetools": {
|
||||
"hashes": [
|
||||
"sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590",
|
||||
"sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==5.3.1"
|
||||
},
|
||||
"cfgv": {
|
||||
"hashes": [
|
||||
"sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426",
|
||||
"sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"
|
||||
],
|
||||
"markers": "python_full_version >= '3.6.1'",
|
||||
"version": "==3.3.1"
|
||||
},
|
||||
"chardet": {
|
||||
"hashes": [
|
||||
"sha256:0d62712b956bc154f85fb0a266e2a3c5913c2967e00348701b32411d6def31e5",
|
||||
"sha256:362777fb014af596ad31334fde1e8c327dfdb076e1960d1694662d46a6917ab9"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==5.1.0"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
|
||||
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==8.1.3"
|
||||
},
|
||||
"colorama": {
|
||||
"hashes": [
|
||||
"sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44",
|
||||
"sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"
|
||||
],
|
||||
"markers": "platform_system == 'Windows'",
|
||||
"version": "==0.4.6"
|
||||
},
|
||||
"coverage": {
|
||||
"extras": [
|
||||
"toml"
|
||||
],
|
||||
"hashes": [
|
||||
"sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f",
|
||||
"sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2",
|
||||
"sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a",
|
||||
"sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a",
|
||||
"sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01",
|
||||
"sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6",
|
||||
"sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7",
|
||||
"sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f",
|
||||
"sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02",
|
||||
"sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c",
|
||||
"sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063",
|
||||
"sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a",
|
||||
"sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5",
|
||||
"sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959",
|
||||
"sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97",
|
||||
"sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6",
|
||||
"sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f",
|
||||
"sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9",
|
||||
"sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5",
|
||||
"sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f",
|
||||
"sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562",
|
||||
"sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe",
|
||||
"sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9",
|
||||
"sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f",
|
||||
"sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb",
|
||||
"sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb",
|
||||
"sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1",
|
||||
"sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb",
|
||||
"sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250",
|
||||
"sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e",
|
||||
"sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511",
|
||||
"sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5",
|
||||
"sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59",
|
||||
"sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2",
|
||||
"sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d",
|
||||
"sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3",
|
||||
"sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4",
|
||||
"sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de",
|
||||
"sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9",
|
||||
"sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833",
|
||||
"sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0",
|
||||
"sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9",
|
||||
"sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d",
|
||||
"sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050",
|
||||
"sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d",
|
||||
"sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6",
|
||||
"sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353",
|
||||
"sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb",
|
||||
"sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e",
|
||||
"sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8",
|
||||
"sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495",
|
||||
"sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2",
|
||||
"sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd",
|
||||
"sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27",
|
||||
"sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1",
|
||||
"sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818",
|
||||
"sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4",
|
||||
"sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e",
|
||||
"sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850",
|
||||
"sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==7.2.7"
|
||||
},
|
||||
"dill": {
|
||||
"hashes": [
|
||||
"sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0",
|
||||
"sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"
|
||||
],
|
||||
"markers": "python_version >= '3.11'",
|
||||
"version": "==0.3.6"
|
||||
},
|
||||
"distlib": {
|
||||
"hashes": [
|
||||
"sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46",
|
||||
"sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"
|
||||
],
|
||||
"version": "==0.3.6"
|
||||
},
|
||||
"filelock": {
|
||||
"hashes": [
|
||||
"sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9",
|
||||
"sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.12.0"
|
||||
},
|
||||
"gitdb": {
|
||||
"hashes": [
|
||||
"sha256:6eb990b69df4e15bad899ea868dc46572c3f75339735663b81de79b06f17eb9a",
|
||||
"sha256:c286cf298426064079ed96a9e4a9d39e7f3e9bf15ba60701e95f5492f28415c7"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==4.0.10"
|
||||
},
|
||||
"gitpython": {
|
||||
"hashes": [
|
||||
"sha256:8ce3bcf69adfdf7c7d503e78fd3b1c492af782d58893b650adb2ac8912ddd573",
|
||||
"sha256:f04893614f6aa713a60cbbe1e6a97403ef633103cdd0ef5eb6efe0deb98dbe8d"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.1.31"
|
||||
},
|
||||
"identify": {
|
||||
"hashes": [
|
||||
"sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4",
|
||||
"sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.5.24"
|
||||
},
|
||||
"iniconfig": {
|
||||
"hashes": [
|
||||
"sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3",
|
||||
"sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.0.0"
|
||||
},
|
||||
"isort": {
|
||||
"hashes": [
|
||||
"sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504",
|
||||
"sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.12.0"
|
||||
},
|
||||
"lazy-object-proxy": {
|
||||
"hashes": [
|
||||
"sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382",
|
||||
"sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82",
|
||||
"sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9",
|
||||
"sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494",
|
||||
"sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46",
|
||||
"sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30",
|
||||
"sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63",
|
||||
"sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4",
|
||||
"sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae",
|
||||
"sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be",
|
||||
"sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701",
|
||||
"sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd",
|
||||
"sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006",
|
||||
"sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a",
|
||||
"sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586",
|
||||
"sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8",
|
||||
"sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821",
|
||||
"sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07",
|
||||
"sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b",
|
||||
"sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171",
|
||||
"sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b",
|
||||
"sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2",
|
||||
"sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7",
|
||||
"sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4",
|
||||
"sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8",
|
||||
"sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e",
|
||||
"sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f",
|
||||
"sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda",
|
||||
"sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4",
|
||||
"sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e",
|
||||
"sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671",
|
||||
"sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11",
|
||||
"sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455",
|
||||
"sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734",
|
||||
"sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb",
|
||||
"sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.9.0"
|
||||
},
|
||||
"markdown-it-py": {
|
||||
"hashes": [
|
||||
"sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30",
|
||||
"sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.2.0"
|
||||
},
|
||||
"mccabe": {
|
||||
"hashes": [
|
||||
"sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325",
|
||||
"sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==0.7.0"
|
||||
},
|
||||
"mdurl": {
|
||||
"hashes": [
|
||||
"sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8",
|
||||
"sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.1.2"
|
||||
},
|
||||
"mypy": {
|
||||
"hashes": [
|
||||
"sha256:1c4c42c60a8103ead4c1c060ac3cdd3ff01e18fddce6f1016e08939647a0e703",
|
||||
"sha256:44797d031a41516fcf5cbfa652265bb994e53e51994c1bd649ffcd0c3a7eccbf",
|
||||
"sha256:473117e310febe632ddf10e745a355714e771ffe534f06db40702775056614c4",
|
||||
"sha256:4c99c3ecf223cf2952638da9cd82793d8f3c0c5fa8b6ae2b2d9ed1e1ff51ba85",
|
||||
"sha256:550a8b3a19bb6589679a7c3c31f64312e7ff482a816c96e0cecec9ad3a7564dd",
|
||||
"sha256:658fe7b674769a0770d4b26cb4d6f005e88a442fe82446f020be8e5f5efb2fae",
|
||||
"sha256:6e33bb8b2613614a33dff70565f4c803f889ebd2f859466e42b46e1df76018dd",
|
||||
"sha256:6e42d29e324cdda61daaec2336c42512e59c7c375340bd202efa1fe0f7b8f8ca",
|
||||
"sha256:74bc9b6e0e79808bf8678d7678b2ae3736ea72d56eede3820bd3849823e7f305",
|
||||
"sha256:76ec771e2342f1b558c36d49900dfe81d140361dd0d2df6cd71b3db1be155409",
|
||||
"sha256:7d23370d2a6b7a71dc65d1266f9a34e4cde9e8e21511322415db4b26f46f6b8c",
|
||||
"sha256:87df44954c31d86df96c8bd6e80dfcd773473e877ac6176a8e29898bfb3501cb",
|
||||
"sha256:8c5979d0deb27e0f4479bee18ea0f83732a893e81b78e62e2dda3e7e518c92ee",
|
||||
"sha256:95d8d31a7713510685b05fbb18d6ac287a56c8f6554d88c19e73f724a445448a",
|
||||
"sha256:a22435632710a4fcf8acf86cbd0d69f68ac389a3892cb23fbad176d1cddaf228",
|
||||
"sha256:a8763e72d5d9574d45ce5881962bc8e9046bf7b375b0abf031f3e6811732a897",
|
||||
"sha256:c1eb485cea53f4f5284e5baf92902cd0088b24984f4209e25981cc359d64448d",
|
||||
"sha256:c5d2cc54175bab47011b09688b418db71403aefad07cbcd62d44010543fc143f",
|
||||
"sha256:cbc07246253b9e3d7d74c9ff948cd0fd7a71afcc2b77c7f0a59c26e9395cb152",
|
||||
"sha256:d0b6c62206e04061e27009481cb0ec966f7d6172b5b936f3ead3d74f29fe3dcf",
|
||||
"sha256:ddae0f39ca146972ff6bb4399f3b2943884a774b8771ea0a8f50e971f5ea5ba8",
|
||||
"sha256:e1f4d16e296f5135624b34e8fb741eb0eadedca90862405b1f1fde2040b9bd11",
|
||||
"sha256:e86c2c6852f62f8f2b24cb7a613ebe8e0c7dc1402c61d36a609174f63e0ff017",
|
||||
"sha256:ebc95f8386314272bbc817026f8ce8f4f0d2ef7ae44f947c4664efac9adec929",
|
||||
"sha256:f9dca1e257d4cc129517779226753dbefb4f2266c4eaad610fc15c6a7e14283e",
|
||||
"sha256:faff86aa10c1aa4a10e1a301de160f3d8fc8703b88c7e98de46b531ff1276a9a"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.3.0"
|
||||
},
|
||||
"mypy-extensions": {
|
||||
"hashes": [
|
||||
"sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d",
|
||||
"sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.0.0"
|
||||
},
|
||||
"nodeenv": {
|
||||
"hashes": [
|
||||
"sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2",
|
||||
"sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'",
|
||||
"version": "==1.8.0"
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61",
|
||||
"sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==23.1"
|
||||
},
|
||||
"pathspec": {
|
||||
"hashes": [
|
||||
"sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687",
|
||||
"sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.11.1"
|
||||
},
|
||||
"pbr": {
|
||||
"hashes": [
|
||||
"sha256:567f09558bae2b3ab53cb3c1e2e33e726ff3338e7bae3db5dc954b3a44eef12b",
|
||||
"sha256:aefc51675b0b533d56bb5fd1c8c6c0522fe31896679882e1c4c63d5e4a0fccb3"
|
||||
],
|
||||
"markers": "python_version >= '2.6'",
|
||||
"version": "==5.11.1"
|
||||
},
|
||||
"platformdirs": {
|
||||
"hashes": [
|
||||
"sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f",
|
||||
"sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.5.1"
|
||||
},
|
||||
"pluggy": {
|
||||
"hashes": [
|
||||
"sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
|
||||
"sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==1.0.0"
|
||||
},
|
||||
"pre-commit": {
|
||||
"hashes": [
|
||||
"sha256:66e37bec2d882de1f17f88075047ef8962581f83c234ac08da21a0c58953d1f0",
|
||||
"sha256:8056bc52181efadf4aac792b1f4f255dfd2fb5a350ded7335d251a68561e8cb6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.3.2"
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c",
|
||||
"sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.15.1"
|
||||
},
|
||||
"pylint": {
|
||||
"hashes": [
|
||||
"sha256:5dcf1d9e19f41f38e4e85d10f511e5b9c35e1aa74251bf95cdd8cb23584e2db1",
|
||||
"sha256:7a1145fb08c251bdb5cca11739722ce64a63db479283d10ce718b2460e54123c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.17.4"
|
||||
},
|
||||
"pyproject-api": {
|
||||
"hashes": [
|
||||
"sha256:435f46547a9ff22cf4208ee274fca3e2869aeb062a4834adfc99a4dd64af3cf9",
|
||||
"sha256:4698a3777c2e0f6b624f8a4599131e2a25376d90fe8d146d7ac74c67c6f97c43"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.5.1"
|
||||
},
|
||||
"pytest": {
|
||||
"hashes": [
|
||||
"sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362",
|
||||
"sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==7.3.1"
|
||||
},
|
||||
"pytest-cov": {
|
||||
"hashes": [
|
||||
"sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6",
|
||||
"sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.1.0"
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf",
|
||||
"sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293",
|
||||
"sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b",
|
||||
"sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57",
|
||||
"sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b",
|
||||
"sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4",
|
||||
"sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07",
|
||||
"sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba",
|
||||
"sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9",
|
||||
"sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287",
|
||||
"sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513",
|
||||
"sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0",
|
||||
"sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782",
|
||||
"sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0",
|
||||
"sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92",
|
||||
"sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f",
|
||||
"sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2",
|
||||
"sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc",
|
||||
"sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1",
|
||||
"sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c",
|
||||
"sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86",
|
||||
"sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4",
|
||||
"sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c",
|
||||
"sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34",
|
||||
"sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b",
|
||||
"sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d",
|
||||
"sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c",
|
||||
"sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb",
|
||||
"sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7",
|
||||
"sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737",
|
||||
"sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3",
|
||||
"sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d",
|
||||
"sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358",
|
||||
"sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53",
|
||||
"sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78",
|
||||
"sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803",
|
||||
"sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a",
|
||||
"sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f",
|
||||
"sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174",
|
||||
"sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==6.0"
|
||||
},
|
||||
"rich": {
|
||||
"hashes": [
|
||||
"sha256:76f6b65ea7e5c5d924ba80e322231d7cb5b5981aa60bfc1e694f1bc097fe6fe1",
|
||||
"sha256:d204aadb50b936bf6b1a695385429d192bc1fdaf3e8b907e8e26f4c4e4b5bf75"
|
||||
],
|
||||
"markers": "python_full_version >= '3.7.0'",
|
||||
"version": "==13.4.1"
|
||||
},
|
||||
"ruff": {
|
||||
"hashes": [
|
||||
"sha256:0012f9b7dc137ab7f1f0355e3c4ca49b562baf6c9fa1180948deeb6648c52957",
|
||||
"sha256:08188f8351f4c0b6216e8463df0a76eb57894ca59a3da65e4ed205db980fd3ae",
|
||||
"sha256:0827b074635d37984fc98d99316bfab5c8b1231bb83e60dacc83bd92883eedb4",
|
||||
"sha256:0bbfbf6fd2436165566ca85f6e57be03ed2f0a994faf40180cfbb3604c9232ef",
|
||||
"sha256:0d61ae4841313f6eeb8292dc349bef27b4ce426e62c36e80ceedc3824e408734",
|
||||
"sha256:0eb412f20e77529a01fb94d578b19dcb8331b56f93632aa0cce4a2ea27b7aeba",
|
||||
"sha256:21f00e47ab2308617c44435c8dfd9e2e03897461c9e647ec942deb2a235b4cfd",
|
||||
"sha256:3ed3b198768d2b3a2300fb18f730cd39948a5cc36ba29ae9d4639a11040880be",
|
||||
"sha256:643de865fd35cb76c4f0739aea5afe7b8e4d40d623df7e9e6ea99054e5cead0a",
|
||||
"sha256:739495d2dbde87cf4e3110c8d27bc20febf93112539a968a4e02c26f0deccd1d",
|
||||
"sha256:8af391ef81f7be960be10886a3c1aac0b298bde7cb9a86ec2b05faeb2081ce6b",
|
||||
"sha256:95db07b7850b30ebf32b27fe98bc39e0ab99db3985edbbf0754d399eb2f0e690",
|
||||
"sha256:9613456b0b375766244c25045e353bc8890c856431cd97893c97b10cc93bd28d",
|
||||
"sha256:b4c037fe2f75bcd9aed0c89c7c507cb7fa59abae2bd4c8b6fc331a28178655a4",
|
||||
"sha256:b775e2c5fc869359daf8c8b8aa0fd67240201ab2e8d536d14a0edf279af18786",
|
||||
"sha256:eca02e709b3308eb7255b5f74e779be23b5980fca3862eae28bb23069cd61ae4",
|
||||
"sha256:f74c4d550f7b8e808455ac77bbce38daafc458434815ba0bc21ae4bdb276509b"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.0.270"
|
||||
},
|
||||
"setuptools": {
|
||||
"hashes": [
|
||||
"sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f",
|
||||
"sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==67.8.0"
|
||||
},
|
||||
"smmap": {
|
||||
"hashes": [
|
||||
"sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94",
|
||||
"sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==5.0.0"
|
||||
},
|
||||
"stevedore": {
|
||||
"hashes": [
|
||||
"sha256:8cc040628f3cea5d7128f2e76cf486b2251a4e543c7b938f58d9a377f6694a2d",
|
||||
"sha256:a54534acf9b89bc7ed264807013b505bf07f74dbe4bcfa37d32bd063870b087c"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==5.1.0"
|
||||
},
|
||||
"tomlkit": {
|
||||
"hashes": [
|
||||
"sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171",
|
||||
"sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.11.8"
|
||||
},
|
||||
"tox": {
|
||||
"hashes": [
|
||||
"sha256:ad87fb7a10ef476afb6eb7e408808057f42976ef0d30ad5fe023099ba493ce58",
|
||||
"sha256:f1a9541b292aa0449f6c7bb67dc0073f25f9086413c3922fe47f5168cbf7b2f4"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.5.2"
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c",
|
||||
"sha256:3a8b36f13dd5fdc5d1b16fe317f5668545de77fa0b8e02006381fd49d731ab98"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==4.6.2"
|
||||
},
|
||||
"vermin": {
|
||||
"hashes": [
|
||||
"sha256:420995de564ac0c31e2157220259d7ac82556e8fa69c112d8005b78c14b0caf5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.5.1"
|
||||
},
|
||||
"virtualenv": {
|
||||
"hashes": [
|
||||
"sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e",
|
||||
"sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==20.23.0"
|
||||
},
|
||||
"wrapt": {
|
||||
"hashes": [
|
||||
"sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0",
|
||||
"sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420",
|
||||
"sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a",
|
||||
"sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c",
|
||||
"sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079",
|
||||
"sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923",
|
||||
"sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f",
|
||||
"sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1",
|
||||
"sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8",
|
||||
"sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86",
|
||||
"sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0",
|
||||
"sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364",
|
||||
"sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e",
|
||||
"sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c",
|
||||
"sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e",
|
||||
"sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c",
|
||||
"sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727",
|
||||
"sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff",
|
||||
"sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e",
|
||||
"sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29",
|
||||
"sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7",
|
||||
"sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72",
|
||||
"sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475",
|
||||
"sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a",
|
||||
"sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317",
|
||||
"sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2",
|
||||
"sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd",
|
||||
"sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640",
|
||||
"sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98",
|
||||
"sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248",
|
||||
"sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e",
|
||||
"sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d",
|
||||
"sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec",
|
||||
"sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1",
|
||||
"sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e",
|
||||
"sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9",
|
||||
"sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92",
|
||||
"sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb",
|
||||
"sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094",
|
||||
"sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46",
|
||||
"sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29",
|
||||
"sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd",
|
||||
"sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705",
|
||||
"sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8",
|
||||
"sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975",
|
||||
"sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb",
|
||||
"sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e",
|
||||
"sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b",
|
||||
"sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418",
|
||||
"sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019",
|
||||
"sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1",
|
||||
"sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba",
|
||||
"sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6",
|
||||
"sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2",
|
||||
"sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3",
|
||||
"sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7",
|
||||
"sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752",
|
||||
"sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416",
|
||||
"sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f",
|
||||
"sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1",
|
||||
"sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc",
|
||||
"sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145",
|
||||
"sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee",
|
||||
"sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a",
|
||||
"sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7",
|
||||
"sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b",
|
||||
"sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653",
|
||||
"sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0",
|
||||
"sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90",
|
||||
"sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29",
|
||||
"sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6",
|
||||
"sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034",
|
||||
"sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09",
|
||||
"sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559",
|
||||
"sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"
|
||||
],
|
||||
"markers": "python_version >= '3.11'",
|
||||
"version": "==1.15.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
7
examples/same.md
Normal file
7
examples/same.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Heading
|
||||
|
||||
* a
|
||||
* b
|
||||
* b.a
|
||||
* b.b
|
||||
* c
|
||||
@@ -1,3 +1,9 @@
|
||||
"""
|
||||
markdown-to-json
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
from ._metadata import version as __version__, author as __author__
|
||||
from ._metadata import author as __author__
|
||||
from ._metadata import version as __version__
|
||||
|
||||
__all__ = ["__author__", "__version__"]
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of the markdown_to_json package
|
||||
# Written by Nate Vack <njvack@freshforever.net>
|
||||
# Copyright 2015 Board of Regents of the University of Wisconsin System
|
||||
"""
|
||||
Part of the markdown_to_json package
|
||||
Written by Nate Vack <njvack@freshforever.net>
|
||||
Copyright 2015 Board of Regents of the University of Wisconsin System
|
||||
"""
|
||||
|
||||
version = "1.0.0"
|
||||
# TODO, switch to dunder meta.
|
||||
# pylint: disable=invalid-name
|
||||
version = "1.2.0"
|
||||
author = "Nathan Vack"
|
||||
author_email = "njvack@wisc.edu"
|
||||
# pylint: disable=redefined-builtin
|
||||
license = "MIT"
|
||||
copyright = "Copyright 2015 Boards of Regent of the University of Wisconsin System"
|
||||
url = "https://github.com/njvack/bids-json-writer"
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of the markdown_to_json package
|
||||
# Written by Nate Vack <njvack@freshforever.net>
|
||||
# Copyright 2015 Board of Regents of the University of Wisconsin System
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from functools import reduce
|
||||
import operator
|
||||
|
||||
from .vendor.ordereddict import OrderedDict
|
||||
|
||||
"""
|
||||
Part of the markdown_to_json package
|
||||
Written by Nate Vack <njvack@freshforever.net>
|
||||
Copyright 2015 Board of Regents of the University of Wisconsin System
|
||||
|
||||
This module contains a class to change a CommonMark.py AST into a nested
|
||||
OrderedDict structure. Its rules:
|
||||
|
||||
@@ -50,17 +42,32 @@ structure from CMarkASTNester into an OrderedDict with strings as keys and
|
||||
strings, lists, or OrderedDicts as values.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
class CMarkASTNester(object):
|
||||
def __init__(self):
|
||||
super(CMarkASTNester, self).__init__()
|
||||
import operator
|
||||
from functools import reduce
|
||||
from typing import Any, Optional, Union
|
||||
|
||||
def nest(self, ast):
|
||||
from .vendor.CommonMark.CommonMark import Block
|
||||
from .vendor.ordereddict import OrderedDict
|
||||
|
||||
|
||||
class CMarkASTNester:
|
||||
"""Nests DOM into a python dictionary"""
|
||||
|
||||
# def __init__(self):
|
||||
# super(CMarkASTNester, self).__init__()
|
||||
|
||||
def nest(self, ast: Block):
|
||||
"""Outermost next call"""
|
||||
return self._dictify_blocks(ast.children, 1)
|
||||
|
||||
def _dictify_blocks(self, blocks, heading_level):
|
||||
def _dictify_blocks(self, blocks: Block, heading_level: int):
|
||||
"""Recursive nest call"""
|
||||
|
||||
def matches_heading(block):
|
||||
return block.t == 'ATXHeader' and block.level == heading_level
|
||||
"""Filter function to match headers"""
|
||||
return block.t == "ATXHeader" and block.level == heading_level
|
||||
|
||||
if not any((matches_heading(b) for b in blocks)):
|
||||
self._ensure_list_singleton(blocks)
|
||||
@@ -72,24 +79,24 @@ class CMarkASTNester(object):
|
||||
return splitted
|
||||
|
||||
def _ensure_list_singleton(self, blocks):
|
||||
lists = [e for e in blocks if e.t == 'List']
|
||||
"""Make sure lists don't mix content"""
|
||||
lists = [e for e in blocks if e.t == "List"]
|
||||
if len(blocks) > 1 and len(lists) > 0:
|
||||
l = lists[0]
|
||||
raise ContentError(
|
||||
"Error at line {0}: Can't mix lists and other content".format(
|
||||
l.start_line))
|
||||
first_item = lists[0]
|
||||
raise ContentError("Error at line {0}: Can't mix lists and other content".format(first_item.start_line))
|
||||
|
||||
|
||||
class ContentError(ValueError):
|
||||
pass
|
||||
"""Content Error"""
|
||||
|
||||
|
||||
def dictify_list_by(l, fx):
|
||||
def dictify_list_by(list_of_blocks: list[Any], filter_function) -> dict[Any, Any]:
|
||||
"""Turn list of tokens into dictionary of lists of tokens."""
|
||||
result = OrderedDict()
|
||||
cur = None
|
||||
children = []
|
||||
for item in l:
|
||||
if fx(item):
|
||||
for item in list_of_blocks:
|
||||
if filter_function(item):
|
||||
if cur:
|
||||
# Pop cur, children into result
|
||||
result[cur] = children
|
||||
@@ -102,49 +109,58 @@ def dictify_list_by(l, fx):
|
||||
return result
|
||||
|
||||
|
||||
class Renderer(object):
|
||||
def __init__(self):
|
||||
super(Renderer, self).__init__()
|
||||
class Renderer:
|
||||
"""Processes DOM"""
|
||||
|
||||
def stringify_dict(self, d):
|
||||
out = OrderedDict(
|
||||
[
|
||||
(self._render_block(k), self._valuify(v))
|
||||
for k, v in d.items()
|
||||
])
|
||||
# def __init__(self):
|
||||
# super(Renderer, self).__init__()
|
||||
|
||||
def stringify_dict(self, dictionary: dict[Any, Any]) -> OrderedDict:
|
||||
"""Create dictionary of keys and values as strings"""
|
||||
out = OrderedDict([(self._render_block(k), self._valuify(v)) for k, v in dictionary.items()])
|
||||
return out
|
||||
|
||||
def _valuify(self, cm_vals):
|
||||
if hasattr(cm_vals, 'items'):
|
||||
def _valuify(self, cm_vals: Any) -> Any:
|
||||
"""Render values of dictionary as scalars or lists"""
|
||||
if hasattr(cm_vals, "items"):
|
||||
return self.stringify_dict(cm_vals)
|
||||
if len(cm_vals) == 0:
|
||||
return ''
|
||||
return ""
|
||||
first = cm_vals[0]
|
||||
if first.t == 'List':
|
||||
if first.t == "List":
|
||||
return self._render_List(first)
|
||||
return "\n\n".join([self._render_block(v) for v in cm_vals])
|
||||
|
||||
def _render_block(self, block):
|
||||
def _render_block(self, block: Block):
|
||||
"""Render any block"""
|
||||
method_name = "_render_{0}".format(block.t)
|
||||
method = self._render_generic_block
|
||||
if hasattr(self, method_name):
|
||||
method = getattr(self, method_name)
|
||||
return method(block)
|
||||
|
||||
def _render_generic_block(self, block):
|
||||
if hasattr(block, 'strings') and len(block.strings) > 0:
|
||||
tmp = []
|
||||
for n in block.strings:
|
||||
tmp.append(n.decode('utf8'))
|
||||
return "\n".join(tmp)
|
||||
# function name called based on block type
|
||||
# pylint: disable=invalid-name
|
||||
def _render_generic_block(self, block: Block) -> Optional[Union[str, list[Any]]]:
|
||||
"""Render any block"""
|
||||
if hasattr(block, "strings") and len(block.strings) > 0:
|
||||
return "\n".join(item.decode("utf8") if isinstance(item, bytes) else item for item in block.strings)
|
||||
if len(block.children) > 0:
|
||||
return [self._render_block(b) for b in block.children]
|
||||
# Is this an error state?
|
||||
return None
|
||||
|
||||
def _render_List(self, block):
|
||||
# function name called based on block type
|
||||
# pylint: disable=invalid-name
|
||||
def _render_List(self, block: Block):
|
||||
"""Render list"""
|
||||
# We need to de-nest this one level -- we'll use the trick that
|
||||
# lists can be added to do this.
|
||||
list_items = [self._render_block(li) for li in block.children]
|
||||
return reduce(operator.add, list_items)
|
||||
|
||||
def _render_FencedCode(self, block):
|
||||
# function name called based on block type
|
||||
# pylint: disable=invalid-name
|
||||
def _render_FencedCode(self, block: Block) -> str:
|
||||
"""Render code"""
|
||||
return "```\n" + block.string_content + "```"
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of the markdown_to_json package
|
||||
# Written by Nate Vack <njvack@freshforever.net>
|
||||
# Copyright 2015 Board of Regents of the University of Wisconsin System
|
||||
|
||||
"""Translate markdown into JSON.
|
||||
|
||||
Usage:
|
||||
@@ -18,81 +16,82 @@ Options:
|
||||
most compact possible JSON. the [default: 2]
|
||||
"""
|
||||
|
||||
from __future__ import print_function, absolute_import, unicode_literals
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import json
|
||||
import logging
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
import json
|
||||
from typing import Optional
|
||||
|
||||
import markdown_to_json
|
||||
from markdown_to_json.vendor.docopt import docopt
|
||||
from markdown_to_json.markdown_to_json import CMarkASTNester, Renderer
|
||||
from markdown_to_json.vendor import CommonMark
|
||||
from markdown_to_json.vendor.docopt import docopt
|
||||
|
||||
from markdown_to_json.markdown_to_json import Renderer, CMarkASTNester
|
||||
|
||||
import logging
|
||||
logging.basicConfig(
|
||||
format="%(message)s", stream=sys.stderr, level=logging.INFO)
|
||||
logging.basicConfig(format="%(message)s", stream=sys.stderr, level=logging.INFO)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def writable_io_or_stdout(filename):
|
||||
def writable_io_or_stdout(filename: Optional[str]):
|
||||
"""Switch between file and stdout"""
|
||||
if filename is None:
|
||||
yield sys.stdout
|
||||
return
|
||||
else:
|
||||
|
||||
try:
|
||||
file = open(filename, "w", encoding="utf8")
|
||||
yield file
|
||||
file.close()
|
||||
# pylint: disable=bare-except
|
||||
except:
|
||||
logging.error("Error: Can't open {0} for writing".format(filename))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_markdown_ast(markdown_file: str):
|
||||
"""Parse AST"""
|
||||
# pylint: disable=bare-except
|
||||
with open(markdown_file, "r", encoding="utf8") as file:
|
||||
try:
|
||||
f = open(filename, 'w', encoding='utf8')
|
||||
yield f
|
||||
f.close()
|
||||
return CommonMark.DocParser().parse(file.read())
|
||||
except:
|
||||
logging.error("Error: Can't open {0} for writing".format(
|
||||
filename))
|
||||
logging.error("Error: Can't open {0} for reading".format(markdown_file))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_markdown_ast(markdown_file):
|
||||
try:
|
||||
f = open(markdown_file, 'r', encoding='utf8')
|
||||
return CommonMark.DocParser().parse(f.read())
|
||||
except:
|
||||
logging.error("Error: Can't open {0} for reading".format(
|
||||
markdown_file))
|
||||
sys.exit(1)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
|
||||
def jsonify_markdown(markdown_file, outfile, indent):
|
||||
def jsonify_markdown(markdown_file: str, outfile: Optional[str], indent: int) -> int:
|
||||
"""Jsonify the markdown"""
|
||||
nester = CMarkASTNester()
|
||||
renderer = Renderer()
|
||||
with writable_io_or_stdout(outfile) as f:
|
||||
with writable_io_or_stdout(outfile) as file:
|
||||
ast = get_markdown_ast(markdown_file)
|
||||
nested = nester.nest(ast)
|
||||
rendered = renderer.stringify_dict(nested)
|
||||
json.dump(rendered, f, indent=indent, ensure_ascii=False)
|
||||
f.write("\n")
|
||||
json.dump(rendered, file, indent=indent, ensure_ascii=False)
|
||||
file.write("\n")
|
||||
return 0
|
||||
|
||||
|
||||
def main(args=[]):
|
||||
def main():
|
||||
pargs = docopt(
|
||||
__doc__,
|
||||
version="md_to_json {0}".format(markdown_to_json.__version__,),)
|
||||
indent = -1
|
||||
version="md_to_json {0}".format(
|
||||
markdown_to_json.__version__,
|
||||
),
|
||||
)
|
||||
# indent = -1
|
||||
|
||||
try:
|
||||
indent = int(pargs.get('-i'))
|
||||
indent = int(pargs.get("-i"))
|
||||
# pylint: disable=bare-except
|
||||
except:
|
||||
logging.error("Error: Indent must be a number")
|
||||
sys.exit(1)
|
||||
if indent < 0:
|
||||
indent = None
|
||||
return jsonify_markdown(
|
||||
pargs['<markdown_file>'],
|
||||
pargs.get('-o'),
|
||||
indent)
|
||||
return jsonify_markdown(pargs["<markdown_file>"], pargs.get("-o"), indent)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
||||
791
markdown_to_json/vendor/CommonMark/CommonMark.py
vendored
791
markdown_to_json/vendor/CommonMark/CommonMark.py
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,4 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
__all__ = ["HTMLRenderer", "DocParser", "dumpAST", "ASTtoJSON"]
|
||||
from .CommonMark import HTMLRenderer
|
||||
from .CommonMark import DocParser
|
||||
from .CommonMark import dumpAST
|
||||
from .CommonMark import ASTtoJSON
|
||||
from .CommonMark import ASTtoJSON, DocParser, HTMLRenderer, dumpAST
|
||||
|
||||
4716
markdown_to_json/vendor/CommonMark/entitytrans.py
vendored
4716
markdown_to_json/vendor/CommonMark/entitytrans.py
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,41 +1,68 @@
|
||||
#!/usr/bin/env python
|
||||
from __future__ import division
|
||||
import re, time, codecs, argparse, sys
|
||||
|
||||
import argparse
|
||||
import codecs
|
||||
import pprint
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
|
||||
import CommonMark
|
||||
|
||||
|
||||
class colors:
|
||||
HEADER = '\033[95m'
|
||||
OKBLUE = '\033[94m'
|
||||
OKGREEN = '\033[92m'
|
||||
WARNING = '\033[93m'
|
||||
FAIL = '\033[91m'
|
||||
ENDC = '\033[0m'
|
||||
HEADER = "\033[95m"
|
||||
OKBLUE = "\033[94m"
|
||||
OKGREEN = "\033[92m"
|
||||
WARNING = "\033[93m"
|
||||
FAIL = "\033[91m"
|
||||
ENDC = "\033[0m"
|
||||
|
||||
|
||||
def trace_calls(frame, event, arg):
|
||||
co = frame.f_code
|
||||
func_name = co.co_name
|
||||
if func_name == "write":
|
||||
return
|
||||
line_no = frame.f_lineno
|
||||
filename = co.co_filename
|
||||
if event == "call" and not re.match("__", func_name) and re.search("CommonMark.py", filename) and not func_name == "dumpAST":
|
||||
print("-> "+frame.f_back.f_code.co_name+" at "+str(frame.f_back.f_lineno)+" called "+func_name+" at "+str(line_no)+" in "+filename)
|
||||
return trace_calls
|
||||
return
|
||||
co = frame.f_code
|
||||
func_name = co.co_name
|
||||
if func_name == "write":
|
||||
return
|
||||
line_no = frame.f_lineno
|
||||
filename = co.co_filename
|
||||
if (
|
||||
event == "call"
|
||||
and not re.match("__", func_name)
|
||||
and re.search("CommonMark.py", filename)
|
||||
and not func_name == "dumpAST"
|
||||
):
|
||||
print(
|
||||
"-> "
|
||||
+ frame.f_back.f_code.co_name
|
||||
+ " at "
|
||||
+ str(frame.f_back.f_lineno)
|
||||
+ " called "
|
||||
+ func_name
|
||||
+ " at "
|
||||
+ str(line_no)
|
||||
+ " in "
|
||||
+ filename
|
||||
)
|
||||
return trace_calls
|
||||
return
|
||||
|
||||
parser = argparse.ArgumentParser(description="script to run the CommonMark specification tests against the CommonMark.py parser")
|
||||
parser.add_argument('-t', help="Single test to run or comma seperated list of tests (-t 10 or -t 10,11,12,13)")
|
||||
parser.add_argument('-p', action="store_true", help="Print passed test information")
|
||||
parser.add_argument('-f', action="store_true", help="Print failed tests (during -np...)")
|
||||
parser.add_argument('-i', action="store_true", help="Interactive Markdown input mode")
|
||||
parser.add_argument('-d', action="store_true", help="Debug, trace calls")
|
||||
parser.add_argument('-np', action="store_true", help="Only print section header, tick, or cross")
|
||||
parser.add_argument('-s', action="store_true", help="Print percent of tests passed by category")
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="script to run the CommonMark specification tests against the CommonMark.py parser"
|
||||
)
|
||||
parser.add_argument("-t", help="Single test to run or comma seperated list of tests (-t 10 or -t 10,11,12,13)")
|
||||
parser.add_argument("-p", action="store_true", help="Print passed test information")
|
||||
parser.add_argument("-f", action="store_true", help="Print failed tests (during -np...)")
|
||||
parser.add_argument("-i", action="store_true", help="Interactive Markdown input mode")
|
||||
parser.add_argument("-d", action="store_true", help="Debug, trace calls")
|
||||
parser.add_argument("-np", action="store_true", help="Only print section header, tick, or cross")
|
||||
parser.add_argument("-s", action="store_true", help="Print percent of tests passed by category")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.d:
|
||||
sys.settrace(trace_calls)
|
||||
sys.settrace(trace_calls)
|
||||
|
||||
renderer = CommonMark.HTMLRenderer()
|
||||
parser = CommonMark.DocParser()
|
||||
@@ -43,7 +70,7 @@ parser = CommonMark.DocParser()
|
||||
f = codecs.open("spec.txt", encoding="utf-8")
|
||||
datalist = []
|
||||
for line in f:
|
||||
datalist.append(line)
|
||||
datalist.append(line)
|
||||
data = "".join(datalist)
|
||||
passed = 0
|
||||
failed = 0
|
||||
@@ -51,107 +78,145 @@ catStats = {}
|
||||
examples = []
|
||||
example_number = 0
|
||||
current_section = ""
|
||||
tabChar = u'\u2192'
|
||||
spaceChar = u'\u2423'
|
||||
nbspChar = u'\u00A0'
|
||||
tabChar = "\u2192"
|
||||
spaceChar = "\u2423"
|
||||
nbspChar = "\u00A0"
|
||||
|
||||
|
||||
def showSpaces(t):
|
||||
t = re.sub(u"\\t", tabChar, t)
|
||||
t = re.sub(u" ", spaceChar, t)
|
||||
t = re.sub(nbspChar, spaceChar, t)
|
||||
return t
|
||||
t = re.sub("\\t", tabChar, t)
|
||||
t = re.sub(" ", spaceChar, t)
|
||||
t = re.sub(nbspChar, spaceChar, t)
|
||||
return t
|
||||
|
||||
|
||||
t = re.sub("\r\n", "\n", data)
|
||||
|
||||
tests = re.sub("^<!-- END TESTS -->(.|[\n])*", '', t, flags=re.M)
|
||||
tests = re.sub("^<!-- END TESTS -->(.|[\n])*", "", t, flags=re.M)
|
||||
testMatch = re.findall(re.compile("^\.\n([\s\S]*?)^\.\n([\s\S]*?)^\.$|^#{1,6} *(.*)$", re.M), tests)
|
||||
|
||||
for match in testMatch:
|
||||
if not match[2] == "":
|
||||
current_section = match[2]
|
||||
else:
|
||||
example_number += 1
|
||||
examples.append({'markdown': match[0], 'html': match[1], 'section': current_section, 'number': example_number})
|
||||
if not match[2] == "":
|
||||
current_section = match[2]
|
||||
else:
|
||||
example_number += 1
|
||||
examples.append({"markdown": match[0], "html": match[1], "section": current_section, "number": example_number})
|
||||
|
||||
current_section = ""
|
||||
|
||||
startTime = time.clock()
|
||||
|
||||
if args.i:
|
||||
print(colors.OKGREEN+"(To end input of Markdown block enter 'end' on it's own line, to quit enter 'quit')"+colors.ENDC)
|
||||
while True:
|
||||
s = ""
|
||||
while True:
|
||||
inp = raw_input(colors.OKBLUE+"Markdown: "+colors.ENDC)
|
||||
if not inp == "end" and not inp == "quit":
|
||||
s += inp+"\n"
|
||||
elif inp == "end":
|
||||
s = s[:-1]
|
||||
break
|
||||
elif inp =="quit":
|
||||
print(colors.HEADER+"bye!"+colors.ENDC)
|
||||
exit(0)
|
||||
ast = parser.parse(s)
|
||||
html = renderer.render(ast)
|
||||
print(colors.WARNING+"="*10+"AST====="+colors.ENDC)
|
||||
CommonMark.dumpAST(ast)
|
||||
print(colors.WARNING+"="*10+"HTML===="+colors.ENDC)
|
||||
print(html)
|
||||
print(
|
||||
colors.OKGREEN
|
||||
+ "(To end input of Markdown block enter 'end' on it's own line, to quit enter 'quit')"
|
||||
+ colors.ENDC
|
||||
)
|
||||
while True:
|
||||
s = ""
|
||||
while True:
|
||||
inp = raw_input(colors.OKBLUE + "Markdown: " + colors.ENDC)
|
||||
if not inp == "end" and not inp == "quit":
|
||||
s += inp + "\n"
|
||||
elif inp == "end":
|
||||
s = s[:-1]
|
||||
break
|
||||
elif inp == "quit":
|
||||
print(colors.HEADER + "bye!" + colors.ENDC)
|
||||
exit(0)
|
||||
ast = parser.parse(s)
|
||||
html = renderer.render(ast)
|
||||
print(colors.WARNING + "=" * 10 + "AST=====" + colors.ENDC)
|
||||
CommonMark.dumpAST(ast)
|
||||
print(colors.WARNING + "=" * 10 + "HTML====" + colors.ENDC)
|
||||
print(html)
|
||||
|
||||
# some tests?
|
||||
if args.t:
|
||||
tests = args.t.split(",")
|
||||
choice_examples = []
|
||||
for t in tests:
|
||||
if not t == "" and len(examples) > int(t):
|
||||
choice_examples.append(examples[int(t)-1])
|
||||
examples = choice_examples
|
||||
tests = args.t.split(",")
|
||||
choice_examples = []
|
||||
for t in tests:
|
||||
if not t == "" and len(examples) > int(t):
|
||||
choice_examples.append(examples[int(t) - 1])
|
||||
examples = choice_examples
|
||||
|
||||
# all tests
|
||||
|
||||
for i, example in enumerate(examples): # [0,examples[0]]
|
||||
if not example['section'] == "" and not current_section == example['section']:
|
||||
print(colors.HEADER+"["+example['section']+"]"+colors.ENDC)
|
||||
current_section = example['section']
|
||||
catStats.update({current_section:[0,0,0]})
|
||||
for i, example in enumerate(examples): # [0,examples[0]]
|
||||
if not example["section"] == "" and not current_section == example["section"]:
|
||||
print(colors.HEADER + "[" + example["section"] + "]" + colors.ENDC)
|
||||
current_section = example["section"]
|
||||
catStats.update({current_section: [0, 0, 0]})
|
||||
|
||||
|
||||
catStats[current_section][2] += 1
|
||||
if args.d: print(colors.HEADER+"[Parsing]"+colors.ENDC)
|
||||
ast = parser.parse(re.sub(tabChar, "\t", example['markdown']))
|
||||
if args.d: print(colors.HEADER+"[Rendering]"+colors.ENDC)
|
||||
actual = renderer.render(ast)
|
||||
if actual == example['html']:
|
||||
passed += 1
|
||||
catStats[current_section][0] += 1
|
||||
if args.t:
|
||||
if not args.f: print("Test #"+str(args.t.split(",")[i]))
|
||||
else:
|
||||
if not args.f: print("Test #"+str(i+1))
|
||||
if not args.f: print(colors.OKGREEN+"tick"+colors.ENDC)
|
||||
if args.d: CommonMark.dumpAST(ast)
|
||||
if args.p or args.d and not args.np:
|
||||
print(colors.OKBLUE+"=== markdown ===============\n"+colors.ENDC+showSpaces(example['markdown'])+colors.OKBLUE+"\n=== expected ===============\n"+colors.ENDC+showSpaces(example['html'])+colors.OKBLUE+"\n=== got ====================\n"+colors.ENDC+showSpaces(actual))
|
||||
else:
|
||||
failed += 1
|
||||
catStats[current_section][1] += 1
|
||||
if args.t:
|
||||
print("Test #"+str(args.t.split(",")[i]))
|
||||
else:
|
||||
print("Test #"+str(i+1))
|
||||
print(colors.FAIL+"cross"+colors.ENDC)
|
||||
if args.d: CommonMark.dumpAST(ast)
|
||||
if not args.np or args.f:
|
||||
print(colors.WARNING+"=== markdown ===============\n"+colors.ENDC+showSpaces(example['markdown'])+colors.WARNING+"\n=== expected ===============\n"+colors.ENDC+showSpaces(example['html'])+colors.WARNING+"\n=== got ====================\n"+colors.ENDC+showSpaces(actual))
|
||||
catStats[current_section][2] += 1
|
||||
if args.d:
|
||||
print(colors.HEADER + "[Parsing]" + colors.ENDC)
|
||||
ast = parser.parse(re.sub(tabChar, "\t", example["markdown"]))
|
||||
if args.d:
|
||||
print(colors.HEADER + "[Rendering]" + colors.ENDC)
|
||||
actual = renderer.render(ast)
|
||||
if actual == example["html"]:
|
||||
passed += 1
|
||||
catStats[current_section][0] += 1
|
||||
if args.t:
|
||||
if not args.f:
|
||||
print("Test #" + str(args.t.split(",")[i]))
|
||||
else:
|
||||
if not args.f:
|
||||
print("Test #" + str(i + 1))
|
||||
if not args.f:
|
||||
print(colors.OKGREEN + "tick" + colors.ENDC)
|
||||
if args.d:
|
||||
CommonMark.dumpAST(ast)
|
||||
if args.p or args.d and not args.np:
|
||||
print(
|
||||
colors.OKBLUE
|
||||
+ "=== markdown ===============\n"
|
||||
+ colors.ENDC
|
||||
+ showSpaces(example["markdown"])
|
||||
+ colors.OKBLUE
|
||||
+ "\n=== expected ===============\n"
|
||||
+ colors.ENDC
|
||||
+ showSpaces(example["html"])
|
||||
+ colors.OKBLUE
|
||||
+ "\n=== got ====================\n"
|
||||
+ colors.ENDC
|
||||
+ showSpaces(actual)
|
||||
)
|
||||
else:
|
||||
failed += 1
|
||||
catStats[current_section][1] += 1
|
||||
if args.t:
|
||||
print("Test #" + str(args.t.split(",")[i]))
|
||||
else:
|
||||
print("Test #" + str(i + 1))
|
||||
print(colors.FAIL + "cross" + colors.ENDC)
|
||||
if args.d:
|
||||
CommonMark.dumpAST(ast)
|
||||
if not args.np or args.f:
|
||||
print(
|
||||
colors.WARNING
|
||||
+ "=== markdown ===============\n"
|
||||
+ colors.ENDC
|
||||
+ showSpaces(example["markdown"])
|
||||
+ colors.WARNING
|
||||
+ "\n=== expected ===============\n"
|
||||
+ colors.ENDC
|
||||
+ showSpaces(example["html"])
|
||||
+ colors.WARNING
|
||||
+ "\n=== got ====================\n"
|
||||
+ colors.ENDC
|
||||
+ showSpaces(actual)
|
||||
)
|
||||
|
||||
print(str(passed)+" tests passed, "+str(failed)+" failed")
|
||||
print(str(passed) + " tests passed, " + str(failed) + " failed")
|
||||
|
||||
endTime = time.clock()
|
||||
runTime = endTime-startTime
|
||||
runTime = endTime - startTime
|
||||
|
||||
if args.s:
|
||||
for i in catStats.keys():
|
||||
per = catStats[i][0]/catStats[i][2]
|
||||
print(colors.HEADER+"["+i+"]"+colors.ENDC+"\t"+str(per*100)+"% Passed")
|
||||
for i in catStats.keys():
|
||||
per = catStats[i][0] / catStats[i][2]
|
||||
print(colors.HEADER + "[" + i + "]" + colors.ENDC + "\t" + str(per * 100) + "% Passed")
|
||||
|
||||
print("runtime: "+str(runTime)+"s")
|
||||
print("runtime: " + str(runTime) + "s")
|
||||
|
||||
158
markdown_to_json/vendor/docopt/docopt.py
vendored
158
markdown_to_json/vendor/docopt/docopt.py
vendored
@@ -6,12 +6,11 @@
|
||||
* Copyright (c) 2013 Vladimir Keleshev, vladimir@keleshev.com
|
||||
|
||||
"""
|
||||
import sys
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
__all__ = ['docopt']
|
||||
__version__ = '0.6.1'
|
||||
__all__ = ["docopt"]
|
||||
__version__ = "0.6.1"
|
||||
|
||||
|
||||
class DocoptLanguageError(Exception):
|
||||
@@ -23,14 +22,13 @@ class DocoptExit(SystemExit):
|
||||
|
||||
"""Exit in case user invoked program with incorrect arguments."""
|
||||
|
||||
usage = ''
|
||||
usage = ""
|
||||
|
||||
def __init__(self, message=''):
|
||||
SystemExit.__init__(self, (message + '\n' + self.usage).strip())
|
||||
def __init__(self, message=""):
|
||||
SystemExit.__init__(self, (message + "\n" + self.usage).strip())
|
||||
|
||||
|
||||
class Pattern(object):
|
||||
|
||||
def __eq__(self, other):
|
||||
return repr(self) == repr(other)
|
||||
|
||||
@@ -44,11 +42,11 @@ class Pattern(object):
|
||||
|
||||
def fix_identities(self, uniq=None):
|
||||
"""Make pattern-tree tips point to same object if they are equal."""
|
||||
if not hasattr(self, 'children'):
|
||||
if not hasattr(self, "children"):
|
||||
return self
|
||||
uniq = list(set(self.flat())) if uniq is None else uniq
|
||||
for i, child in enumerate(self.children):
|
||||
if not hasattr(child, 'children'):
|
||||
if not hasattr(child, "children"):
|
||||
assert child in uniq
|
||||
self.children[i] = uniq[uniq.index(child)]
|
||||
else:
|
||||
@@ -104,7 +102,7 @@ class LeafPattern(Pattern):
|
||||
self.name, self.value = name, value
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r, %r)' % (self.__class__.__name__, self.name, self.value)
|
||||
return "%s(%r, %r)" % (self.__class__.__name__, self.name, self.value)
|
||||
|
||||
def flat(self, *types):
|
||||
return [self] if not types or type(self) in types else []
|
||||
@@ -114,14 +112,13 @@ class LeafPattern(Pattern):
|
||||
pos, match = self.single_match(left)
|
||||
if match is None:
|
||||
return False, left, collected
|
||||
left_ = left[:pos] + left[pos + 1:]
|
||||
left_ = left[:pos] + left[pos + 1 :]
|
||||
same_name = [a for a in collected if a.name == self.name]
|
||||
if type(self.value) in (int, list):
|
||||
if type(self.value) is int:
|
||||
increment = 1
|
||||
else:
|
||||
increment = ([match.value] if type(match.value) is str
|
||||
else match.value)
|
||||
increment = [match.value] if type(match.value) is str else match.value
|
||||
if not same_name:
|
||||
match.value = increment
|
||||
return True, left_, collected + [match]
|
||||
@@ -138,8 +135,7 @@ class BranchPattern(Pattern):
|
||||
self.children = list(children)
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%s)' % (self.__class__.__name__,
|
||||
', '.join(repr(a) for a in self.children))
|
||||
return "%s(%s)" % (self.__class__.__name__, ", ".join(repr(a) for a in self.children))
|
||||
|
||||
def flat(self, *types):
|
||||
if type(self) in types:
|
||||
@@ -148,7 +144,6 @@ class BranchPattern(Pattern):
|
||||
|
||||
|
||||
class Argument(LeafPattern):
|
||||
|
||||
def single_match(self, left):
|
||||
for n, pattern in enumerate(left):
|
||||
if type(pattern) is Argument:
|
||||
@@ -157,13 +152,12 @@ class Argument(LeafPattern):
|
||||
|
||||
@classmethod
|
||||
def parse(class_, source):
|
||||
name = re.findall('(<\S*?>)', source)[0]
|
||||
value = re.findall('\[default: (.*)\]', source, flags=re.I)
|
||||
name = re.findall("(<\S*?>)", source)[0]
|
||||
value = re.findall("\[default: (.*)\]", source, flags=re.I)
|
||||
return class_(name, value[0] if value else None)
|
||||
|
||||
|
||||
class Command(Argument):
|
||||
|
||||
def __init__(self, name, value=False):
|
||||
self.name, self.value = name, value
|
||||
|
||||
@@ -178,7 +172,6 @@ class Command(Argument):
|
||||
|
||||
|
||||
class Option(LeafPattern):
|
||||
|
||||
def __init__(self, short=None, long=None, argcount=0, value=False):
|
||||
assert argcount in (0, 1)
|
||||
self.short, self.long, self.argcount = short, long, argcount
|
||||
@@ -187,17 +180,17 @@ class Option(LeafPattern):
|
||||
@classmethod
|
||||
def parse(class_, option_description):
|
||||
short, long, argcount, value = None, None, 0, False
|
||||
options, _, description = option_description.strip().partition(' ')
|
||||
options = options.replace(',', ' ').replace('=', ' ')
|
||||
options, _, description = option_description.strip().partition(" ")
|
||||
options = options.replace(",", " ").replace("=", " ")
|
||||
for s in options.split():
|
||||
if s.startswith('--'):
|
||||
if s.startswith("--"):
|
||||
long = s
|
||||
elif s.startswith('-'):
|
||||
elif s.startswith("-"):
|
||||
short = s
|
||||
else:
|
||||
argcount = 1
|
||||
if argcount:
|
||||
matched = re.findall('\[default: (.*)\]', description, flags=re.I)
|
||||
matched = re.findall("\[default: (.*)\]", description, flags=re.I)
|
||||
value = matched[0] if matched else None
|
||||
return class_(short, long, argcount, value)
|
||||
|
||||
@@ -212,12 +205,10 @@ class Option(LeafPattern):
|
||||
return self.long or self.short
|
||||
|
||||
def __repr__(self):
|
||||
return 'Option(%r, %r, %r, %r)' % (self.short, self.long,
|
||||
self.argcount, self.value)
|
||||
return "Option(%r, %r, %r, %r)" % (self.short, self.long, self.argcount, self.value)
|
||||
|
||||
|
||||
class Required(BranchPattern):
|
||||
|
||||
def match(self, left, collected=None):
|
||||
collected = [] if collected is None else collected
|
||||
l = left
|
||||
@@ -230,7 +221,6 @@ class Required(BranchPattern):
|
||||
|
||||
|
||||
class Optional(BranchPattern):
|
||||
|
||||
def match(self, left, collected=None):
|
||||
collected = [] if collected is None else collected
|
||||
for pattern in self.children:
|
||||
@@ -244,7 +234,6 @@ class OptionsShortcut(Optional):
|
||||
|
||||
|
||||
class OneOrMore(BranchPattern):
|
||||
|
||||
def match(self, left, collected=None):
|
||||
assert len(self.children) == 1
|
||||
collected = [] if collected is None else collected
|
||||
@@ -266,7 +255,6 @@ class OneOrMore(BranchPattern):
|
||||
|
||||
|
||||
class Either(BranchPattern):
|
||||
|
||||
def match(self, left, collected=None):
|
||||
collected = [] if collected is None else collected
|
||||
outcomes = []
|
||||
@@ -280,15 +268,14 @@ class Either(BranchPattern):
|
||||
|
||||
|
||||
class Tokens(list):
|
||||
|
||||
def __init__(self, source, error=DocoptExit):
|
||||
self += source.split() if hasattr(source, 'split') else source
|
||||
self += source.split() if hasattr(source, "split") else source
|
||||
self.error = error
|
||||
|
||||
@staticmethod
|
||||
def from_pattern(source):
|
||||
source = re.sub(r'([\[\]\(\)\|]|\.\.\.)', r' \1 ', source)
|
||||
source = [s for s in re.split('\s+|(\S*<.*?>)', source) if s]
|
||||
source = re.sub(r"([\[\]\(\)\|]|\.\.\.)", r" \1 ", source)
|
||||
source = [s for s in re.split("\s+|(\S*<.*?>)", source) if s]
|
||||
return Tokens(source, error=DocoptLanguageError)
|
||||
|
||||
def move(self):
|
||||
@@ -300,31 +287,29 @@ class Tokens(list):
|
||||
|
||||
def parse_long(tokens, options):
|
||||
"""long ::= '--' chars [ ( ' ' | '=' ) chars ] ;"""
|
||||
long, eq, value = tokens.move().partition('=')
|
||||
assert long.startswith('--')
|
||||
value = None if eq == value == '' else value
|
||||
long, eq, value = tokens.move().partition("=")
|
||||
assert long.startswith("--")
|
||||
value = None if eq == value == "" else value
|
||||
similar = [o for o in options if o.long == long]
|
||||
if tokens.error is DocoptExit and similar == []: # if no exact match
|
||||
similar = [o for o in options if o.long and o.long.startswith(long)]
|
||||
if len(similar) > 1: # might be simply specified ambiguously 2+ times?
|
||||
raise tokens.error('%s is not a unique prefix: %s?' %
|
||||
(long, ', '.join(o.long for o in similar)))
|
||||
raise tokens.error("%s is not a unique prefix: %s?" % (long, ", ".join(o.long for o in similar)))
|
||||
elif len(similar) < 1:
|
||||
argcount = 1 if eq == '=' else 0
|
||||
argcount = 1 if eq == "=" else 0
|
||||
o = Option(None, long, argcount)
|
||||
options.append(o)
|
||||
if tokens.error is DocoptExit:
|
||||
o = Option(None, long, argcount, value if argcount else True)
|
||||
else:
|
||||
o = Option(similar[0].short, similar[0].long,
|
||||
similar[0].argcount, similar[0].value)
|
||||
o = Option(similar[0].short, similar[0].long, similar[0].argcount, similar[0].value)
|
||||
if o.argcount == 0:
|
||||
if value is not None:
|
||||
raise tokens.error('%s must not have an argument' % o.long)
|
||||
raise tokens.error("%s must not have an argument" % o.long)
|
||||
else:
|
||||
if value is None:
|
||||
if tokens.current() in [None, '--']:
|
||||
raise tokens.error('%s requires argument' % o.long)
|
||||
if tokens.current() in [None, "--"]:
|
||||
raise tokens.error("%s requires argument" % o.long)
|
||||
value = tokens.move()
|
||||
if tokens.error is DocoptExit:
|
||||
o.value = value if value is not None else True
|
||||
@@ -334,32 +319,30 @@ def parse_long(tokens, options):
|
||||
def parse_shorts(tokens, options):
|
||||
"""shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ;"""
|
||||
token = tokens.move()
|
||||
assert token.startswith('-') and not token.startswith('--')
|
||||
left = token.lstrip('-')
|
||||
assert token.startswith("-") and not token.startswith("--")
|
||||
left = token.lstrip("-")
|
||||
parsed = []
|
||||
while left != '':
|
||||
short, left = '-' + left[0], left[1:]
|
||||
while left != "":
|
||||
short, left = "-" + left[0], left[1:]
|
||||
similar = [o for o in options if o.short == short]
|
||||
if len(similar) > 1:
|
||||
raise tokens.error('%s is specified ambiguously %d times' %
|
||||
(short, len(similar)))
|
||||
raise tokens.error("%s is specified ambiguously %d times" % (short, len(similar)))
|
||||
elif len(similar) < 1:
|
||||
o = Option(short, None, 0)
|
||||
options.append(o)
|
||||
if tokens.error is DocoptExit:
|
||||
o = Option(short, None, 0, True)
|
||||
else: # why copying is necessary here?
|
||||
o = Option(short, similar[0].long,
|
||||
similar[0].argcount, similar[0].value)
|
||||
o = Option(short, similar[0].long, similar[0].argcount, similar[0].value)
|
||||
value = None
|
||||
if o.argcount != 0:
|
||||
if left == '':
|
||||
if tokens.current() in [None, '--']:
|
||||
raise tokens.error('%s requires argument' % short)
|
||||
if left == "":
|
||||
if tokens.current() in [None, "--"]:
|
||||
raise tokens.error("%s requires argument" % short)
|
||||
value = tokens.move()
|
||||
else:
|
||||
value = left
|
||||
left = ''
|
||||
left = ""
|
||||
if tokens.error is DocoptExit:
|
||||
o.value = value if value is not None else True
|
||||
parsed.append(o)
|
||||
@@ -370,17 +353,17 @@ def parse_pattern(source, options):
|
||||
tokens = Tokens.from_pattern(source)
|
||||
result = parse_expr(tokens, options)
|
||||
if tokens.current() is not None:
|
||||
raise tokens.error('unexpected ending: %r' % ' '.join(tokens))
|
||||
raise tokens.error("unexpected ending: %r" % " ".join(tokens))
|
||||
return Required(*result)
|
||||
|
||||
|
||||
def parse_expr(tokens, options):
|
||||
"""expr ::= seq ( '|' seq )* ;"""
|
||||
seq = parse_seq(tokens, options)
|
||||
if tokens.current() != '|':
|
||||
if tokens.current() != "|":
|
||||
return seq
|
||||
result = [Required(*seq)] if len(seq) > 1 else seq
|
||||
while tokens.current() == '|':
|
||||
while tokens.current() == "|":
|
||||
tokens.move()
|
||||
seq = parse_seq(tokens, options)
|
||||
result += [Required(*seq)] if len(seq) > 1 else seq
|
||||
@@ -390,9 +373,9 @@ def parse_expr(tokens, options):
|
||||
def parse_seq(tokens, options):
|
||||
"""seq ::= ( atom [ '...' ] )* ;"""
|
||||
result = []
|
||||
while tokens.current() not in [None, ']', ')', '|']:
|
||||
while tokens.current() not in [None, "]", ")", "|"]:
|
||||
atom = parse_atom(tokens, options)
|
||||
if tokens.current() == '...':
|
||||
if tokens.current() == "...":
|
||||
atom = [OneOrMore(*atom)]
|
||||
tokens.move()
|
||||
result += atom
|
||||
@@ -401,25 +384,25 @@ def parse_seq(tokens, options):
|
||||
|
||||
def parse_atom(tokens, options):
|
||||
"""atom ::= '(' expr ')' | '[' expr ']' | 'options'
|
||||
| long | shorts | argument | command ;
|
||||
| long | shorts | argument | command ;
|
||||
"""
|
||||
token = tokens.current()
|
||||
result = []
|
||||
if token in '([':
|
||||
if token in "([":
|
||||
tokens.move()
|
||||
matching, pattern = {'(': [')', Required], '[': [']', Optional]}[token]
|
||||
matching, pattern = {"(": [")", Required], "[": ["]", Optional]}[token]
|
||||
result = pattern(*parse_expr(tokens, options))
|
||||
if tokens.move() != matching:
|
||||
raise tokens.error("unmatched '%s'" % token)
|
||||
return [result]
|
||||
elif token == 'options':
|
||||
elif token == "options":
|
||||
tokens.move()
|
||||
return [OptionsShortcut()]
|
||||
elif token.startswith('--') and token != '--':
|
||||
elif token.startswith("--") and token != "--":
|
||||
return parse_long(tokens, options)
|
||||
elif token.startswith('-') and token not in ('-', '--'):
|
||||
elif token.startswith("-") and token not in ("-", "--"):
|
||||
return parse_shorts(tokens, options)
|
||||
elif token.startswith('<') and token.endswith('>') or token.isupper():
|
||||
elif token.startswith("<") and token.endswith(">") or token.isupper():
|
||||
return [Argument(tokens.move())]
|
||||
else:
|
||||
return [Command(tokens.move())]
|
||||
@@ -436,11 +419,11 @@ def parse_argv(tokens, options, options_first=False):
|
||||
"""
|
||||
parsed = []
|
||||
while tokens.current() is not None:
|
||||
if tokens.current() == '--':
|
||||
if tokens.current() == "--":
|
||||
return parsed + [Argument(None, v) for v in tokens]
|
||||
elif tokens.current().startswith('--'):
|
||||
elif tokens.current().startswith("--"):
|
||||
parsed += parse_long(tokens, options)
|
||||
elif tokens.current().startswith('-') and tokens.current() != '-':
|
||||
elif tokens.current().startswith("-") and tokens.current() != "-":
|
||||
parsed += parse_shorts(tokens, options)
|
||||
elif options_first:
|
||||
return parsed + [Argument(None, v) for v in tokens]
|
||||
@@ -451,40 +434,39 @@ def parse_argv(tokens, options, options_first=False):
|
||||
|
||||
def parse_defaults(doc):
|
||||
defaults = []
|
||||
for s in parse_section('options:', doc):
|
||||
for s in parse_section("options:", doc):
|
||||
# FIXME corner case "bla: options: --foo"
|
||||
_, _, s = s.partition(':') # get rid of "options:"
|
||||
split = re.split('\n[ \t]*(-\S+?)', '\n' + s)[1:]
|
||||
_, _, s = s.partition(":") # get rid of "options:"
|
||||
split = re.split("\n[ \t]*(-\S+?)", "\n" + s)[1:]
|
||||
split = [s1 + s2 for s1, s2 in zip(split[::2], split[1::2])]
|
||||
options = [Option.parse(s) for s in split if s.startswith('-')]
|
||||
options = [Option.parse(s) for s in split if s.startswith("-")]
|
||||
defaults += options
|
||||
return defaults
|
||||
|
||||
|
||||
def parse_section(name, source):
|
||||
pattern = re.compile('^([^\n]*' + name + '[^\n]*\n?(?:[ \t].*?(?:\n|$))*)',
|
||||
re.IGNORECASE | re.MULTILINE)
|
||||
pattern = re.compile("^([^\n]*" + name + "[^\n]*\n?(?:[ \t].*?(?:\n|$))*)", re.IGNORECASE | re.MULTILINE)
|
||||
return [s.strip() for s in pattern.findall(source)]
|
||||
|
||||
|
||||
def formal_usage(section):
|
||||
_, _, section = section.partition(':') # drop "usage:"
|
||||
_, _, section = section.partition(":") # drop "usage:"
|
||||
pu = section.split()
|
||||
return '( ' + ' '.join(') | (' if s == pu[0] else s for s in pu[1:]) + ' )'
|
||||
return "( " + " ".join(") | (" if s == pu[0] else s for s in pu[1:]) + " )"
|
||||
|
||||
|
||||
def extras(help, version, options, doc):
|
||||
if help and any((o.name in ('-h', '--help')) and o.value for o in options):
|
||||
if help and any((o.name in ("-h", "--help")) and o.value for o in options):
|
||||
print(doc.strip("\n"))
|
||||
sys.exit()
|
||||
if version and any(o.name == '--version' and o.value for o in options):
|
||||
if version and any(o.name == "--version" and o.value for o in options):
|
||||
print(version)
|
||||
sys.exit()
|
||||
|
||||
|
||||
class Dict(dict):
|
||||
def __repr__(self):
|
||||
return '{%s}' % ',\n '.join('%r: %r' % i for i in sorted(self.items()))
|
||||
return "{%s}" % ",\n ".join("%r: %r" % i for i in sorted(self.items()))
|
||||
|
||||
|
||||
def docopt(doc, argv=None, help=True, version=None, options_first=False):
|
||||
@@ -552,7 +534,7 @@ def docopt(doc, argv=None, help=True, version=None, options_first=False):
|
||||
"""
|
||||
argv = sys.argv[1:] if argv is None else argv
|
||||
|
||||
usage_sections = parse_section('usage:', doc)
|
||||
usage_sections = parse_section("usage:", doc)
|
||||
if len(usage_sections) == 0:
|
||||
raise DocoptLanguageError('"usage:" (case-insensitive) not found.')
|
||||
if len(usage_sections) > 1:
|
||||
@@ -562,7 +544,7 @@ def docopt(doc, argv=None, help=True, version=None, options_first=False):
|
||||
options = parse_defaults(doc)
|
||||
pattern = parse_pattern(formal_usage(DocoptExit.usage), options)
|
||||
# [default] syntax for argument is disabled
|
||||
#for a in pattern.flat(Argument):
|
||||
# for a in pattern.flat(Argument):
|
||||
# same_name = [d for d in arguments if d.name == a.name]
|
||||
# if same_name:
|
||||
# a.value = same_name[0].value
|
||||
@@ -571,7 +553,7 @@ def docopt(doc, argv=None, help=True, version=None, options_first=False):
|
||||
for options_shortcut in pattern.flat(OptionsShortcut):
|
||||
doc_options = parse_defaults(doc)
|
||||
options_shortcut.children = list(set(doc_options) - pattern_options)
|
||||
#if any_options:
|
||||
# if any_options:
|
||||
# options_shortcut.children += [Option(o.short, o.long, o.argcount)
|
||||
# for o in argv if type(o) is Option]
|
||||
extras(help, version, argv, doc)
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
|
||||
from UserDict import DictMixin
|
||||
|
||||
class OrderedDict(dict, DictMixin):
|
||||
|
||||
class OrderedDict(dict, DictMixin):
|
||||
def __init__(self, *args, **kwds):
|
||||
if len(args) > 1:
|
||||
raise TypeError('expected at most 1 arguments, got %d' % len(args))
|
||||
raise TypeError("expected at most 1 arguments, got %d" % len(args))
|
||||
try:
|
||||
self.__end
|
||||
except AttributeError:
|
||||
@@ -35,8 +35,8 @@ class OrderedDict(dict, DictMixin):
|
||||
|
||||
def clear(self):
|
||||
self.__end = end = []
|
||||
end += [None, end, end] # sentinel node for doubly linked list
|
||||
self.__map = {} # key --> [key, prev, next]
|
||||
end += [None, end, end] # sentinel node for doubly linked list
|
||||
self.__map = {} # key --> [key, prev, next]
|
||||
dict.clear(self)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
@@ -68,7 +68,7 @@ class OrderedDict(dict, DictMixin):
|
||||
|
||||
def popitem(self, last=True):
|
||||
if not self:
|
||||
raise KeyError('dictionary is empty')
|
||||
raise KeyError("dictionary is empty")
|
||||
if last:
|
||||
key = reversed(self).next()
|
||||
else:
|
||||
@@ -100,8 +100,8 @@ class OrderedDict(dict, DictMixin):
|
||||
|
||||
def __repr__(self):
|
||||
if not self:
|
||||
return '%s()' % (self.__class__.__name__,)
|
||||
return '%s(%r)' % (self.__class__.__name__, self.items())
|
||||
return "%s()" % (self.__class__.__name__,)
|
||||
return "%s(%r)" % (self.__class__.__name__, self.items())
|
||||
|
||||
def copy(self):
|
||||
return self.__class__(self)
|
||||
@@ -117,7 +117,7 @@ class OrderedDict(dict, DictMixin):
|
||||
if isinstance(other, OrderedDict):
|
||||
if len(self) != len(other):
|
||||
return False
|
||||
for p, q in zip(self.items(), other.items()):
|
||||
for p, q in zip(self.items(), other.items()):
|
||||
if p != q:
|
||||
return False
|
||||
return True
|
||||
|
||||
151
pyproject.toml
Normal file
151
pyproject.toml
Normal file
@@ -0,0 +1,151 @@
|
||||
[tool.poetry]
|
||||
name = "markdown_to_json"
|
||||
version = "1.2.0"
|
||||
description = "Lossy python to markdown serializer"
|
||||
|
||||
authors = ["Nathan Vack <njvack@wisc.edu>"]
|
||||
keywords = ["serializer", "deserializer", "markdown",]
|
||||
classifiers = [
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
|
||||
]
|
||||
include = [
|
||||
"markdown_to_json/**/*.py",
|
||||
"markdown_to_json/**/*.md",
|
||||
"markdown_to_json/**/*.txt",
|
||||
"markdown_to_json/**/*.html",
|
||||
"markdown_to_json/**/*.jinja",
|
||||
]
|
||||
exclude = [
|
||||
]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/njvack/markdown_to_json"
|
||||
homepage = "https://github.com/njvack/markdown_to_json"
|
||||
documentation ="https://github.com/njvack/markdown_to_json"
|
||||
|
||||
[tool.poetry.urls]
|
||||
"Bug Tracker" = "https://github.com/matthewdeanmartin/markdown_to_json/issues"
|
||||
"Change Log" = "https://github.com/matthewdeanmartin/markdown_to_json/blob/main/CHANGES.md"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pytest-cov = "*"
|
||||
pytest = "*"
|
||||
vermin = "*"
|
||||
tox = "*"
|
||||
isort = "*"
|
||||
bandit = "*"
|
||||
black = "*"
|
||||
pylint = "*"
|
||||
ruff = "*"
|
||||
mypy = "*"
|
||||
pre-commit = "*"
|
||||
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
target-version = ['py39']
|
||||
include = '\.pyi?$'
|
||||
exclude = '''
|
||||
|
||||
(
|
||||
/(
|
||||
\.eggs # exclude a few common directories in the
|
||||
| \.git # root of the project
|
||||
| \.hg
|
||||
| \.mypy_cache
|
||||
| \.tox
|
||||
| \.venv
|
||||
| _build
|
||||
| buck-out
|
||||
| build
|
||||
| dist
|
||||
)/
|
||||
| foo.py # also separately exclude a file named foo.py in
|
||||
# the root of the project
|
||||
)
|
||||
'''
|
||||
[build-system]
|
||||
requires = ["poetry>=0.12"]
|
||||
build-backend = "poetry.masonry.api"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
minversion = "6.0"
|
||||
testpaths = [
|
||||
"tests",
|
||||
]
|
||||
junit_family = "xunit1"
|
||||
norecursedirs = ["vendor", "scripts"]
|
||||
# don't know how to do this in toml
|
||||
#addopts = "--strict-markers"
|
||||
#markers =
|
||||
# slow: marks tests as slow (deselect with '-m "not slow"')
|
||||
# fast: marks tests as fast (deselect with '-m "not fast"')
|
||||
|
||||
[tool.isort]
|
||||
default_section = "THIRDPARTY"
|
||||
force_grid_wrap = 0
|
||||
include_trailing_comma = true
|
||||
known_first_party = ["markdown_to_json"]
|
||||
line_length = 88
|
||||
multi_line_output = 3
|
||||
use_parentheses = true
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 1000
|
||||
|
||||
# Enable Pyflakes `E` and `F` codes by default.
|
||||
select = ["E", "F"]
|
||||
ignore = [
|
||||
"E722",
|
||||
"E501",
|
||||
]
|
||||
|
||||
# Exclude a variety of commonly ignored directories.
|
||||
exclude = [
|
||||
"markdown_to_json/vendor",
|
||||
"dead_code",
|
||||
".bzr",
|
||||
".direnv",
|
||||
".eggs",
|
||||
".git",
|
||||
".hg",
|
||||
".mypy_cache",
|
||||
".nox",
|
||||
".pants.d",
|
||||
".ruff_cache",
|
||||
".svn",
|
||||
".tox",
|
||||
".venv",
|
||||
"__pypackages__",
|
||||
"_build",
|
||||
"buck-out",
|
||||
"build",
|
||||
"dist",
|
||||
"node_modules",
|
||||
"venv",
|
||||
"hide"
|
||||
]
|
||||
per-file-ignores = { }
|
||||
|
||||
# Allow unused variables when underscore-prefixed.
|
||||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
|
||||
# Assume Python 3.10.
|
||||
target-version = "py311"
|
||||
|
||||
[tool.scriv]
|
||||
format = "md"
|
||||
version = "literal: pyproject.toml: project.version"
|
||||
|
||||
[tool.hatch.version]
|
||||
path = "markdown_to_json/__init__.py"
|
||||
output_file = "CHANGELOG.${config:format}"
|
||||
38
setup.py
38
setup.py
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from setuptools import setup, find_packages, Command
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
packages = find_packages()
|
||||
|
||||
@@ -19,28 +19,32 @@ class PyTest(Command):
|
||||
def run(self):
|
||||
import sys
|
||||
import subprocess
|
||||
errno = subprocess.call([sys.executable, 'runtests.py'])
|
||||
|
||||
errno = subprocess.call([sys.executable, "runtests.py"])
|
||||
raise SystemExit(errno)
|
||||
|
||||
|
||||
def get_locals(filename):
|
||||
l = {}
|
||||
exec(open(filename, 'r').read(), {}, l)
|
||||
return l
|
||||
the_locals = {}
|
||||
exec(open(filename, "r").read(), {}, the_locals)
|
||||
return the_locals
|
||||
|
||||
metadata = get_locals(os.path.join('markdown_to_json', '_metadata.py'))
|
||||
|
||||
metadata = get_locals(os.path.join("markdown_to_json", "_metadata.py"))
|
||||
|
||||
this_directory = Path(__file__).parent
|
||||
long_description = (this_directory / "README.md").read_text()
|
||||
|
||||
setup(
|
||||
name="markdown-to-json",
|
||||
version=metadata['version'],
|
||||
author=metadata['author'],
|
||||
author_email=metadata['author_email'],
|
||||
license=metadata['license'],
|
||||
url=metadata['url'],
|
||||
long_description=long_description,
|
||||
long_description_content_type='text/markdown',
|
||||
version=metadata["version"],
|
||||
author=metadata["author"],
|
||||
author_email=metadata["author_email"],
|
||||
license=metadata["license"],
|
||||
url=metadata["url"],
|
||||
packages=find_packages(),
|
||||
cmdclass={'test': PyTest},
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'md_to_json = markdown_to_json.scripts.md_to_json:main'
|
||||
]}
|
||||
)
|
||||
cmdclass={"test": PyTest},
|
||||
entry_points={"console_scripts": ["md_to_json = markdown_to_json.scripts.md_to_json:main"]},
|
||||
)
|
||||
|
||||
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
23
tests/test_example_files.py
Normal file
23
tests/test_example_files.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import glob
|
||||
|
||||
from markdown_to_json.markdown_to_json import Renderer, CMarkASTNester
|
||||
from markdown_to_json.vendor.CommonMark import CommonMark
|
||||
from tests.util import locate_file
|
||||
|
||||
|
||||
def test_examples():
|
||||
absolute_file_paths = []
|
||||
for file in glob.glob(locate_file("../examples", __file__) + "/*.md"):
|
||||
sample_search_results_file: str = locate_file(file, __file__)
|
||||
absolute_file_paths.append(sample_search_results_file)
|
||||
assert absolute_file_paths
|
||||
|
||||
for file_name in absolute_file_paths:
|
||||
if "bad_nest.md" in file_name:
|
||||
# not expecting that one to pass
|
||||
continue
|
||||
with open(file_name, encoding="utf-8") as file:
|
||||
ast = CommonMark.DocParser().parse(file.read())
|
||||
dictionary = CMarkASTNester().nest(ast)
|
||||
stringified = Renderer().stringify_dict(dictionary)
|
||||
assert stringified
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of the markdown_to_json package
|
||||
# Written by Nate Vack <njvack@freshforever.net>
|
||||
# Copyright 2015 Board of Regents of the University of Wisconsin System
|
||||
@@ -30,5 +29,5 @@ def list_nested(list_md):
|
||||
|
||||
def test_nester_lists_correctly(list_nested):
|
||||
stringified = Renderer().stringify_dict(list_nested)
|
||||
l = stringified['Heading']
|
||||
assert l == ['a', 'b', ['b.a', 'b.b'], 'c']
|
||||
list_value = stringified["Heading"]
|
||||
assert list_value == ["a", "b", ["b.a", "b.b"], "c"]
|
||||
|
||||
18
tests/util.py
Normal file
18
tests/util.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""
|
||||
Utilities
|
||||
"""
|
||||
import os
|
||||
|
||||
|
||||
def locate_file(file_name: str, executing_file: str) -> str:
|
||||
"""
|
||||
Find file relative to a source file, e.g.
|
||||
locate("foo/bar.txt", __file__)
|
||||
|
||||
Succeeds regardless to context of execution
|
||||
|
||||
File must exist
|
||||
"""
|
||||
file_path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(executing_file)), file_name))
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
return file_path
|
||||
Reference in New Issue
Block a user