From d39b90d9f5ee3350cd0e46c92e8e4df933a84c69 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Mon, 14 Sep 2026 14:23:05 +0200 Subject: [PATCH] Fix tag parsing bugs, and bring the project setup up to date Three bugs, each with a regression test: - The tag pattern matched its arguments greedily, so two tags on the same line became one match that swallowed the text between them and then failed to parse. Match lazily instead. - parse_argkwarg() decided arg-versus-kwarg on any '=' in a segment, and split on every '=' it found. An '=' inside a value raised a SyntaxError, as in read_csv('a=b.csv') or sep='='. It also tracked brackets and parentheses but not braces, so a dict argument with more than one key was split on its comma. Both now use one scanner that knows what is nested inside quotes, brackets, braces or parentheses. - convert_to_md_table() escaped the columns of the DataFrame passed in before copying it, so a macros user rendering the same DataFrame twice got doubly escaped pipes the second time. Project setup: - Deploy the documentation on every push to master. It was deployed by hand and had fallen behind the readers added in 4.0.0. - Upload coverage to Codecov again: the step tested env.USING_COVERAGE, which is set nowhere, so it never ran. - Move dev dependencies to [dependency-groups], which uv now expects, and drop the unused codecov package. - Add .codespellrc, which the codespell workflow already referred to. - Add the 'enabled' option to schema.json, and a CHANGELOG.md. - Update the Makefile and CONTRIBUTING.md, which still described test_requirements.txt, pyflakes and setup.py. - Link to the hosted docs from the README, where a relative link 404s. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_017vTNbovqPphizF3v1UdhVW --- .codespellrc | 4 + .github/workflows/documentation.yml | 27 ++++ .github/workflows/workflow.yml | 11 +- CHANGELOG.md | 18 +++ CONTRIBUTING.md | 51 ++++--- Makefile | 12 +- README.md | 2 +- docs/schema.json | 6 + pyproject.toml | 7 +- src/mkdocs_table_reader_plugin/__init__.py | 2 +- src/mkdocs_table_reader_plugin/markdown.py | 3 +- src/mkdocs_table_reader_plugin/plugin.py | 6 +- src/mkdocs_table_reader_plugin/safe_eval.py | 127 ++++++++++++------ .../basic_setup/docs/page_read_two_csv.md | 4 + tests/test_build.py | 4 + tests/test_kwargs.py | 5 +- tests/test_markdown.py | 14 ++ tests/test_safe_eval.py | 39 +++++- uv.lock | 15 --- 19 files changed, 252 insertions(+), 105 deletions(-) create mode 100644 .codespellrc create mode 100644 .github/workflows/documentation.yml create mode 100644 CHANGELOG.md diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 0000000..ede7e0e --- /dev/null +++ b/.codespellrc @@ -0,0 +1,4 @@ +[codespell] +# 'sav' is the SPSS file extension, used by read_spss() +ignore-words-list = sav +skip = ./.git,./.venv,./site,./uv.lock,./.ruff_cache,./.pytest_cache,./*.egg-info,./src/*.egg-info,./tests/fixtures/encoding,*.xlsx,*.feather,*.parquet,*.orc,*.dta,*.xpt,*.h5 diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml new file mode 100644 index 0000000..149ae09 --- /dev/null +++ b/.github/workflows/documentation.yml @@ -0,0 +1,27 @@ +name: Deploy documentation + +on: + push: + branches: [master] + workflow_dispatch: + +permissions: + contents: write + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + # git-authors and git-revision-date-localized need the full history + fetch-depth: 0 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + + - name: Deploy to GitHub Pages + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + uv run mkdocs gh-deploy --force diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 06986c9..c825e29 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -4,11 +4,11 @@ jobs: run: runs-on: ${{ matrix.os }} strategy: - matrix: + matrix: os: [ubuntu-latest, macos-latest, windows-latest] python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - - uses: actions/checkout@master + - uses: actions/checkout@v4 - name: Install uv and set the python version uses: astral-sh/setup-uv@v5 @@ -19,11 +19,12 @@ jobs: run: | uv run pytest --cov=mkdocs_table_reader_plugin --cov-report=xml + # Upload once per run, from a single matrix job - name: Upload coverage to Codecov - if: contains(env.USING_COVERAGE, matrix.python-version) && github.ref == 'refs/heads/master' - uses: codecov/codecov-action@v4 + if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13' && github.ref == 'refs/heads/master' + uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} - file: ./coverage.xml + files: ./coverage.xml flags: unittests fail_ci_if_error: true diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..5611bc7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,18 @@ +# Changelog + +Releases before 4.0.1 are described in the [GitHub releases](https://github.com/timvink/mkdocs-table-reader-plugin/releases). + +## 4.0.1 + +Bug fixes: + +- Two reader tags on the same line are now two tables. The tag pattern matched greedily, so it swallowed everything between the first and the last tag on a line, and then failed to parse the result. +- An `=` inside an argument value no longer breaks parsing. `{{ read_csv('a=b.csv') }}`, `sep='='` and `na_values=['a=1']` all raised a `SyntaxError` before. +- A comma inside a dict argument no longer breaks parsing, so `dtype={'a': 'str', 'b': 'int'}` works. Lists and tuples already worked. +- `convert_to_md_table()` no longer escapes pipe characters in the DataFrame you pass in. When a `mkdocs-macros-plugin` user rendered the same DataFrame twice, the second table showed doubly escaped pipes. + +Project maintenance: + +- The documentation site now deploys on every push to `master`. It was deployed by hand, and had fallen behind the readers added in 4.0.0. +- Coverage is uploaded to Codecov again. The upload step tested an environment variable that was never set, so it never ran. +- `enabled` is included in `schema.json`, so editors stop flagging it as an unknown option. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fd6e4a9..d7b0665 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,46 +7,55 @@ Thanks for considering to contribute to this project! Some guidelines: - This package tries to be as simple as possible for the user (hide any complexity from the user). Options are only added when there is clear value to the majority of users. - When issues or pull requests are not going to be resolved or merged, they should be closed as soon as possible. This is kinder than deciding this after a long period. Our issue tracker should reflect work to be done. -## Testing +## Development setup -Make sure to install an editable version before running tests: +This project uses [uv](https://docs.astral.sh/uv/). Install the project and its development dependencies with: -```python -pip install -r tests/test_requirements.txt -pip install -e . -pytest --cov=mkdocs_table_reader_plugin --cov-report term-missing tests +```bash +uv sync ``` -If it makes sense, writing tests for your PRs is always appreciated and will help get them merged. +## Testing + +Run the unit tests and the linter with: + +```bash +make test +``` -In addition, this project uses pyflakes for static code checking: +Or separately: -```python -pip install pyflakes -pyflakes tests/ mkdocs_table_reader_plugin/ +```bash +uv run pytest --cov=mkdocs_table_reader_plugin --cov-report term-missing tests +uv run ruff check src/ tests/ ``` -#### Code Style +If it makes sense, writing tests for your PRs is always appreciated and will help get them merged. + +### Code Style -Make sure your code *roughly* follows [PEP-8](https://www.python.org/dev/peps/pep-0008/) and keeps things consistent with the rest of the code. +Make sure your code *roughly* follows [PEP-8](https://www.python.org/dev/peps/pep-0008/) and keeps things consistent with the rest of the code. `ruff` is configured in `pyproject.toml` and fixes what it can automatically. We use google-style docstrings. ## Documentation -They need to be deployed manually: +Preview the documentation site locally with: ```bash -mkdocs gh-deploy --force +make serve_docs ``` +Every push to `master` deploys the site to GitHub Pages through the `documentation.yml` workflow. You can also deploy by hand with `make deploy_docs`. + ## Release -Update `setup.py`. +1. Update `__version__` in `src/mkdocs_table_reader_plugin/__init__.py` and add an entry to `CHANGELOG.md`. +2. Commit, then tag and push: -```bash -git tag -git push origin -``` + ```bash + git tag v + git push origin master --tags + ``` -Then manually create a github release to trigger publishing to pypi. +3. Create a GitHub release for the tag. That triggers the `pythonpublish.yml` workflow, which runs the tests and publishes to PyPI. diff --git a/Makefile b/Makefile index 2b1ac96..65aff08 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,13 @@ setup: - pip install -r tests/test_requirements.txt - pip install -e . + uv sync test: - pyflakes tests/ mkdocs_table_reader_plugin/ - pytest --cov=mkdocs_table_reader_plugin --cov-report term-missing tests + uv run ruff check src/ tests/ + uv run pytest --cov=mkdocs_table_reader_plugin --cov-report term-missing tests + +serve_docs: + uv run mkdocs serve deploy_docs: - mkdocs gh-deploy --force \ No newline at end of file + uv run mkdocs gh-deploy --force diff --git a/README.md b/README.md index 39c383b..51b7ee1 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ In your markdown files you can now use: Where the path is relative to the location of your project's `mkdocs.yml` file, _or_ your project's `docs/` directory, _or_ the location of your markdown source file (all 3 possible locations will be searched, in that order). - There are [readers](https://timvink.github.io/mkdocs-table-reader-plugin/readers/) available for many common table formats, like `.csv`, `.fwf`, `.json`, `.xls`, `.xlsx`, `.yaml`, `.feather`, `.tsv`, `.parquet`, `.orc`, `.html` and `.xml`, as well as HDF5, SPSS, SAS and Stata files. There is also the `read_raw()` reader that will allow you to insert tables (or other content) already in markdown format. -- `table-reader` is compatible with [`mkdocs-macros-plugin`](https://mkdocs-macros-plugin.readthedocs.io/en/latest/). This enables further automation like filtering tables or inserting directories of tables. See the documentation on [compatibility with macros plugin](howto/use_jinja2.md) for more examples. +- `table-reader` is compatible with [`mkdocs-macros-plugin`](https://mkdocs-macros-plugin.readthedocs.io/en/latest/). This enables further automation like filtering tables or inserting directories of tables. See the documentation on [compatibility with macros plugin](https://timvink.github.io/mkdocs-table-reader-plugin/howto/use_jinja2/) for more examples. ## Documentation and how-to guides diff --git a/docs/schema.json b/docs/schema.json index 22c47df..46bc2bf 100644 --- a/docs/schema.json +++ b/docs/schema.json @@ -15,6 +15,12 @@ "markdownDescription": "https://timvink.github.io/mkdocs-table-reader-plugin/options/", "type": "object", "properties": { + "enabled": { + "title": "Enables you to deactivate this plugin.", + "markdownDescription": "https://timvink.github.io/mkdocs-table-reader-plugin/options/#enabled", + "type": "boolean", + "default": true + }, "data_path": { "title": "Additional path to search", "markdownDescription": "https://timvink.github.io/mkdocs-table-reader-plugin/options/#data_path", diff --git a/pyproject.toml b/pyproject.toml index 1df106b..4430a36 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,10 +67,8 @@ lint.ignore = ['D104' ,'E722' ,'D104' ,'E402' - ,"UP038" # UP038 Use `X | Y` in `isinstance` call instead of `(X, Y)` ] -# Exclude files in tests dir lint.exclude = [ ".bzr", ".direnv", @@ -112,10 +110,9 @@ target-version = "py310" # Always autofix fix = true -[tool.uv] -dev-dependencies = [ +[dependency-groups] +dev = [ "click>=8.1.8", - "codecov>=2.1.13", # used by pd.read_html() and pd.read_xml() "lxml>=5", "mkdocs-git-authors-plugin>=0.9.4", diff --git a/src/mkdocs_table_reader_plugin/__init__.py b/src/mkdocs_table_reader_plugin/__init__.py index 150ee9c..76ad18b 100644 --- a/src/mkdocs_table_reader_plugin/__init__.py +++ b/src/mkdocs_table_reader_plugin/__init__.py @@ -1 +1 @@ -__version__ = "4.0.0" \ No newline at end of file +__version__ = "4.0.1" diff --git a/src/mkdocs_table_reader_plugin/markdown.py b/src/mkdocs_table_reader_plugin/markdown.py index fcaa952..6fb6e88 100644 --- a/src/mkdocs_table_reader_plugin/markdown.py +++ b/src/mkdocs_table_reader_plugin/markdown.py @@ -62,8 +62,9 @@ def escape(value): value = replace_newlines(value) return value - df.columns = [escape(c) for c in df.columns] + # Escape a copy, so that a DataFrame passed in by a macros user is left alone df = df.map(escape) + df.columns = [escape(c) for c in df.columns] return df.to_markdown(**markdown_kwargs) diff --git a/src/mkdocs_table_reader_plugin/plugin.py b/src/mkdocs_table_reader_plugin/plugin.py index c6519ed..0db7835 100644 --- a/src/mkdocs_table_reader_plugin/plugin.py +++ b/src/mkdocs_table_reader_plugin/plugin.py @@ -48,17 +48,17 @@ def on_config(self, config, **kwargs): mkdocs_config=config, plugin_config=self.config ) for reader in self.config.get("select_readers") - if reader in self.config.get("select_readers", []) } # Regex pattern for tags like {{ read_csv(..) }}, for all selected readers at once, # so that every page is scanned only once, no matter how many readers are selected. # match group 1: to extract any leading whitespace # match group 2: to extract the reader - # match group 3: to extract the arguments (positional and keywords) + # match group 3: to extract the arguments (positional and keywords). Matched + # lazily, so that two tags on the same line are two matches instead of one. # Note that a reader never matches when none are selected self.tag_pattern = re.compile( - r"( *)\{\{\s+(%s)\((.+)\)\s+\}\}" % "|".join(self.readers or ["(?!)"]), # noqa: UP031 + r"( *)\{\{\s+(%s)\((.+?)\)\s+\}\}" % "|".join(self.readers or ["(?!)"]), # noqa: UP031 flags=re.IGNORECASE, ) diff --git a/src/mkdocs_table_reader_plugin/safe_eval.py b/src/mkdocs_table_reader_plugin/safe_eval.py index c41d541..718b643 100644 --- a/src/mkdocs_table_reader_plugin/safe_eval.py +++ b/src/mkdocs_table_reader_plugin/safe_eval.py @@ -28,7 +28,6 @@ """ -import re from ast import literal_eval @@ -55,68 +54,108 @@ def safe_eval(string): return literal_eval(string) -def parse_argkwarg(input_str: str): +def scan(input_str: str): """ - Parses a string to detect both args and kwargs. + Walk through a string, keeping track of what is nested inside something else. - Adapted code from - https://stackoverflow.com/questions/9305387/string-of-kwargs-to-kwargs + A character is top level when it is not inside quotes and not inside + brackets, braces or parentheses. That is what tells a separator between two + arguments apart from the same character inside a value, as in + `read_csv('a=b.csv', dtype={'a': 'str', 'b': 'int'})`. Args: input_str (str): string with positional and keyword arguments - Returns: - args[List], kwargs[Dict] + Yields: + (int, str, bool): position, character, and whether it is top level """ - # below generated by copilot, validated by unit tests - segments = [] - current_segment = "" in_quotes = False quote_char = "" - bracket_count = 0 - tuple_count = 0 - - for char in input_str: - if char in "\"'" and not in_quotes: + depth = 0 + + for position, char in enumerate(input_str): + if in_quotes: + if char == quote_char: + in_quotes = False + quote_char = "" + elif char in "\"'": in_quotes = True quote_char = char - elif char == quote_char and in_quotes: - in_quotes = False - quote_char = "" - elif char == "[": - bracket_count += 1 - elif char == "]": - bracket_count -= 1 - elif char == "(": - tuple_count += 1 - elif char == ")": - tuple_count -= 1 - elif char == "," and not in_quotes and bracket_count == 0 and tuple_count == 0: - segments.append(current_segment.strip()) - current_segment = "" + elif char in "([{": + depth += 1 + elif char in ")]}": + depth -= 1 + else: + yield position, char, depth == 0 continue - current_segment += char + yield position, char, False + + +def split_top_level(input_str: str, separator: str) -> list: + """ + Split a string on a separator, ignoring separators nested inside a value. + + Args: + input_str (str): string with positional and keyword arguments + separator (str): single character to split on + + Returns: + list: the stripped segments between the separators + """ + segments = [] + start = 0 + + for position, char, top_level in scan(input_str): + if char == separator and top_level: + segments.append(input_str[start:position].strip()) + start = position + 1 + + segments.append(input_str[start:].strip()) + return segments + + +def find_top_level(input_str: str, separator: str) -> int: + """ + Find the first separator that is not nested inside a value. + + Args: + input_str (str): a single positional or keyword argument + separator (str): single character to look for + + Returns: + int: the position of the separator, or -1 when there is none + """ + for position, char, top_level in scan(input_str): + if char == separator and top_level: + return position - segments.append(current_segment.strip()) # Add the last segment - # end code generated by copilot, validated by unit tests + return -1 + + +def parse_argkwarg(input_str: str): + """ + Parses a string to detect both args and kwargs. + Args: + input_str (str): string with positional and keyword arguments + + Returns: + args[List], kwargs[Dict] + """ args = [] - kwargs = [] + kwargs = {} - for i in segments: - i = i.strip() - if "=" in i: - kwargs.append(i) - else: - if len(kwargs) != 0: + for segment in split_top_level(input_str, ","): + position = find_top_level(segment, "=") + + if position == -1: + if kwargs: raise AssertionError( f"[table-reader-plugin] Make sure the python in your reader tag is correct: Positional arguments follow keyword arguments in '{input_str}'" ) - args.append(literal_eval(i)) - - # kwargs as dict - kwargs = [re.split(" ?= ?", x) for x in kwargs] - kwargs = dict([(x[0], safe_eval(x[1])) for x in kwargs]) + args.append(literal_eval(segment)) + else: + kwargs[segment[:position].strip()] = safe_eval(segment[position + 1 :].strip()) return args, kwargs diff --git a/tests/fixtures/basic_setup/docs/page_read_two_csv.md b/tests/fixtures/basic_setup/docs/page_read_two_csv.md index 0523106..56677d6 100644 --- a/tests/fixtures/basic_setup/docs/page_read_two_csv.md +++ b/tests/fixtures/basic_setup/docs/page_read_two_csv.md @@ -11,3 +11,7 @@ The latest numbers using `read_table()`: ## table 2 {{ read_table('assets/tables/basic_table2.csv', sep = ',') }} + +## Both on one line + +Two tags on the same line are two tables: {{ read_csv('assets/tables/basic_table.csv') }} and {{ read_csv('assets/tables/basic_table2.csv') }} and that is that. diff --git a/tests/test_build.py b/tests/test_build.py index cb4adc1..a5ae89d 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -142,6 +142,10 @@ def test_table_output(tmp_path): contents = page_with_tag.read_text() assert re.search(r"table1", contents) assert re.search(r"table2", contents) + # Two tags on the same line are two tables, and the text between them survives + assert re.search(r"and that is that", contents) + assert len(re.findall(r"table1", contents)) == 2 + assert len(re.findall(r"table2", contents)) == 2 def test_compatibility_macros_plugin(tmp_path): diff --git a/tests/test_kwargs.py b/tests/test_kwargs.py index 2ddc505..4056849 100644 --- a/tests/test_kwargs.py +++ b/tests/test_kwargs.py @@ -1,7 +1,8 @@ -import pandas as pd -from mkdocs_table_reader_plugin.utils import get_keywords, kwargs_in_func, kwargs_not_in_func +import pandas as pd + from mkdocs_table_reader_plugin.safe_eval import parse_argkwarg +from mkdocs_table_reader_plugin.utils import get_keywords, kwargs_in_func, kwargs_not_in_func def test_kwargs(): diff --git a/tests/test_markdown.py b/tests/test_markdown.py index 5cb4269..e649c66 100644 --- a/tests/test_markdown.py +++ b/tests/test_markdown.py @@ -95,3 +95,17 @@ def test_fix_indentation(): # Rounded down to a multiple of 4 spaces, which is one markdown indentation level assert fix_indentation(table, leading_spaces=" ") == table assert fix_indentation(table, leading_spaces=" ") == fix_indentation(table, leading_spaces=" ") + + +def test_convert_to_md_table_does_not_alter_input(): + """ + Escaping happens on a copy, because macros users can render a DataFrame twice. + """ + df = pd.DataFrame({"a|b": ["x|y"]}) + + first = convert_to_md_table(df) + + assert list(df.columns) == ["a|b"] + assert df.iloc[0, 0] == "x|y" + # So a second render escapes the same pipes once, not twice + assert convert_to_md_table(df) == first diff --git a/tests/test_safe_eval.py b/tests/test_safe_eval.py index c4642a9..96e7762 100644 --- a/tests/test_safe_eval.py +++ b/tests/test_safe_eval.py @@ -1,5 +1,6 @@ import pytest -from mkdocs_table_reader_plugin.safe_eval import safe_eval, parse_argkwarg + +from mkdocs_table_reader_plugin.safe_eval import parse_argkwarg, safe_eval def test_safe_eval0(): @@ -29,7 +30,7 @@ def test_safe_eval4(): def test_safe_eval5(): myString = "None" - assert safe_eval(myString) == None + assert safe_eval(myString) is None def test_parseargkwarg_1(): @@ -92,3 +93,37 @@ def test_parseargkwarg_error(): with pytest.raises(AssertionError): s = "'assets/tables/table.csv', sep = '\r\t', 'another path'" args, kwargs = parse_argkwarg(s) + + +def test_parseargkwarg_equals_sign_in_value(): + """ + An '=' inside a value is not the separator between a key and a value. + """ + args, kwargs = parse_argkwarg("'a=b.csv'") + assert args == ["a=b.csv"] + assert kwargs == {} + + args, kwargs = parse_argkwarg("'table.csv', sep='='") + assert args == ["table.csv"] + assert kwargs == {"sep": "="} + + args, kwargs = parse_argkwarg("'table.csv', na_values=['a=1']") + assert args == ["table.csv"] + assert kwargs == {"na_values": ["a=1"]} + + +def test_parseargkwarg_nested_values(): + """ + A comma inside a list, tuple or dict does not separate two arguments. + """ + args, kwargs = parse_argkwarg("'table.csv', usecols=[0, 1]") + assert args == ["table.csv"] + assert kwargs == {"usecols": [0, 1]} + + args, kwargs = parse_argkwarg("'table.csv', names=('a', 'b')") + assert args == ["table.csv"] + assert kwargs == {"names": ("a", "b")} + + args, kwargs = parse_argkwarg("'table.csv', dtype={'a': 'str', 'b': 'int'}") + assert args == ["table.csv"] + assert kwargs == {"dtype": {"a": "str", "b": "int"}} diff --git a/uv.lock b/uv.lock index 80a1ee2..3130a97 100644 --- a/uv.lock +++ b/uv.lock @@ -336,19 +336,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl", hash = "sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360", size = 125251, upload-time = "2026-08-26T13:33:12.928Z" }, ] -[[package]] -name = "codecov" -version = "2.1.13" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coverage" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2c/bb/594b26d2c85616be6195a64289c578662678afa4910cef2d3ce8417cf73e/codecov-2.1.13.tar.gz", hash = "sha256:2362b685633caeaf45b9951a9b76ce359cd3581dd515b430c6c3f5dfb4d92a8c", size = 21416, upload-time = "2023-04-17T23:11:39.779Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/af/02/18785edcdf6266cdd6c6dc7635f1cbeefd9a5b4c3bb8aff8bd681e9dd095/codecov-2.1.13-py2.py3-none-any.whl", hash = "sha256:c2ca5e51bba9ebb43644c43d0690148a55086f7f5e6fd36170858fa4206744d5", size = 16512, upload-time = "2023-04-17T23:11:37.344Z" }, -] - [[package]] name = "colorama" version = "0.4.6" @@ -1082,7 +1069,6 @@ dependencies = [ [package.dev-dependencies] dev = [ { name = "click" }, - { name = "codecov" }, { name = "lxml" }, { name = "mkdocs-git-authors-plugin" }, { name = "mkdocs-git-revision-date-localized-plugin" }, @@ -1111,7 +1097,6 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ { name = "click", specifier = ">=8.1.8" }, - { name = "codecov", specifier = ">=2.1.13" }, { name = "lxml", specifier = ">=5" }, { name = "mkdocs-git-authors-plugin", specifier = ">=0.9.4" }, { name = "mkdocs-git-revision-date-localized-plugin", specifier = ">=1.4.5" },