Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-aiocsv for openSUSE:Factory checked in at 2025-08-11 13:53:45 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-aiocsv (Old) and /work/SRC/openSUSE:Factory/.python-aiocsv.new.1085 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-aiocsv" Mon Aug 11 13:53:45 2025 rev:3 rq:1298705 version:1.3.2 Changes: -------- --- /work/SRC/openSUSE:Factory/python-aiocsv/python-aiocsv.changes 2024-10-11 17:07:24.935485002 +0200 +++ /work/SRC/openSUSE:Factory/.python-aiocsv.new.1085/python-aiocsv.changes 2025-08-11 13:54:04.277014622 +0200 @@ -1,0 +2,7 @@ +Mon Aug 11 05:06:04 UTC 2025 - Steve Kowalik <steven.kowa...@suse.com> + +- Add BuildRequires on pytest-asyncio due to pytest 8.4.1. +- Switch to github tarball for test files. +- Skip a test broken by cPython changes. + +------------------------------------------------------------------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-aiocsv.spec ++++++ --- /var/tmp/diff_new_pack.86k4oz/_old 2025-08-11 13:54:05.965084263 +0200 +++ /var/tmp/diff_new_pack.86k4oz/_new 2025-08-11 13:54:05.969084428 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-aiocsv # -# Copyright (c) 2024 SUSE LLC +# Copyright (c) 2025 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -15,6 +15,7 @@ # Please submit bugfixes or comments via https://bugs.opensuse.org/ # + %{?sle15_python_module_pythons} Name: python-aiocsv Version: 1.3.2 @@ -22,10 +23,11 @@ Summary: Asynchronous CSV reading/writing in Python License: MIT URL: https://github.com/MKuranowski/aiocsv -Source: https://files.pythonhosted.org/packages/source/a/aiocsv/aiocsv-%{version}.tar.gz +Source: https://github.com/MKuranowski/aiocsv/archive/refs/tags/v%{version}.tar.gz#/aiocsv-%{version}.tar.gz BuildRequires: %{python_module aiofiles} BuildRequires: %{python_module devel >= 3.8} BuildRequires: %{python_module pip} +BuildRequires: %{python_module pytest-asyncio} BuildRequires: %{python_module pytest} BuildRequires: %{python_module setuptools >= 61.0} BuildRequires: %{python_module typing_extensions} @@ -66,7 +68,8 @@ } %check -%pytest_arch +# https://github.com/MKuranowski/aiocsv/issues/33 +%pytest_arch -k 'not test_parsing_weird_quotes_nonnumeric' %files %{python_files} %doc readme.md ++++++ aiocsv-1.3.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiocsv-1.3.2/.clang-format new/aiocsv-1.3.2/.clang-format --- old/aiocsv-1.3.2/.clang-format 1970-01-01 01:00:00.000000000 +0100 +++ new/aiocsv-1.3.2/.clang-format 2024-04-28 12:29:50.000000000 +0200 @@ -0,0 +1,6 @@ +BasedOnStyle: Google +ColumnLimit: 99 +IndentWidth: 4 +DerivePointerAlignment: false +PointerAlignment: Left +QualifierAlignment: Right diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiocsv-1.3.2/.github/workflows/deploy.yml new/aiocsv-1.3.2/.github/workflows/deploy.yml --- old/aiocsv-1.3.2/.github/workflows/deploy.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/aiocsv-1.3.2/.github/workflows/deploy.yml 2024-04-28 12:29:50.000000000 +0200 @@ -0,0 +1,44 @@ +name: Build and upload to PyPI + +on: + push: + tags: + - v* + workflow_dispatch: + +jobs: + build_wheels: + name: Build wheels on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-13] + + steps: + - uses: actions/checkout@v4 + + - name: Build wheels + uses: pypa/cibuildwheel@v2.16.5 + env: + CIBW_ARCHS: auto64 + CIBW_BUILD: cp3*-* + + - uses: actions/upload-artifact@v4 + with: + name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} + path: ./wheelhouse/*.whl + + upload_pypi: + needs: [build_wheels] + runs-on: ubuntu-latest + steps: + - uses: actions/download-artifact@v4 + with: + pattern: cibw-wheels-* + merge-multiple: true + path: dist + + - uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.pypi_password }} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiocsv-1.3.2/.github/workflows/test.yml new/aiocsv-1.3.2/.github/workflows/test.yml --- old/aiocsv-1.3.2/.github/workflows/test.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/aiocsv-1.3.2/.github/workflows/test.yml 2024-04-28 12:29:50.000000000 +0200 @@ -0,0 +1,40 @@ +name: Automatic tests +on: [push, pull_request, workflow_dispatch] +jobs: + tests: + name: Run tests + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: pip install -Ur requirements.dev.txt + - name: Install the library + run: pip install -e . + - name: Run tests + run: pytest + lint: + name: Lint code + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.8 + uses: actions/setup-python@v5 + with: + python-version: "3.8" + - name: Install dependencies + run: pip install -Ur requirements.dev.txt + - name: Check code formatting + run: black --check . + - name: Check imports order + run: isort --check . + - name: Install the library + run: pip install -e . + - name: Run typechecking + run: pyright diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiocsv-1.3.2/.gitignore new/aiocsv-1.3.2/.gitignore --- old/aiocsv-1.3.2/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/aiocsv-1.3.2/.gitignore 2024-04-28 12:29:50.000000000 +0200 @@ -0,0 +1,10 @@ +__pycache__ +.vscode +.venv +.pytest_cache +*.egg-info +build +dist +ignore_* +*.so + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiocsv-1.3.2/PKG-INFO new/aiocsv-1.3.2/PKG-INFO --- old/aiocsv-1.3.2/PKG-INFO 2024-04-28 12:30:03.973903700 +0200 +++ new/aiocsv-1.3.2/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 @@ -1,356 +0,0 @@ -Metadata-Version: 2.1 -Name: aiocsv -Version: 1.3.2 -Author-email: Mikołaj Kuranowski <mkuranowski+pypacka...@gmail.com> -Project-URL: Homepage, https://github.com/MKuranowski/aiocsv -Keywords: async,asynchronous,csv,tsv -Classifier: Development Status :: 5 - Production/Stable -Classifier: License :: OSI Approved :: MIT License -Classifier: Framework :: AsyncIO -Classifier: Programming Language :: Python :: 3 :: Only -Requires-Python: >=3.8 -Description-Content-Type: text/markdown -License-File: LICENSE -Requires-Dist: typing_extensions - -# aiocsv - -Asynchronous CSV reading and writing. - - -## Installation - -`pip install aiocsv`. Python 3.8+ is required. - -This module contains an extension written in C. Pre-build binaries -may not be available for your configuration. You might need a C compiler -and Python headers to install aiocsv. - - -## Usage - -AsyncReader & AsyncDictReader accept any object that has a `read(size: int)` coroutine, -which should return a string. - -AsyncWriter & AsyncDictWriter accept any object that has a `write(b: str)` coroutine. - -Reading is implemented using a custom CSV parser, which should behave exactly like the CPython parser. - -Writing is implemented using the synchronous csv.writer and csv.DictWriter objects - -the serializers write data to a StringIO, and that buffer is then rewritten to the underlying -asynchronous file. - - -## Example - -Example usage with [aiofiles](https://pypi.org/project/aiofiles/). - -```python -import asyncio -import csv - -import aiofiles -from aiocsv import AsyncReader, AsyncDictReader, AsyncWriter, AsyncDictWriter - -async def main(): - # simple reading - async with aiofiles.open("some_file.csv", mode="r", encoding="utf-8", newline="") as afp: - async for row in AsyncReader(afp): - print(row) # row is a list - - # dict reading, tab-separated - async with aiofiles.open("some_other_file.tsv", mode="r", encoding="utf-8", newline="") as afp: - async for row in AsyncDictReader(afp, delimiter="\t"): - print(row) # row is a dict - - # simple writing, "unix"-dialect - async with aiofiles.open("new_file.csv", mode="w", encoding="utf-8", newline="") as afp: - writer = AsyncWriter(afp, dialect="unix") - await writer.writerow(["name", "age"]) - await writer.writerows([ - ["John", 26], ["Sasha", 42], ["Hana", 37] - ]) - - # dict writing, all quoted, "NULL" for missing fields - async with aiofiles.open("new_file2.csv", mode="w", encoding="utf-8", newline="") as afp: - writer = AsyncDictWriter(afp, ["name", "age"], restval="NULL", quoting=csv.QUOTE_ALL) - await writer.writeheader() - await writer.writerow({"name": "John", "age": 26}) - await writer.writerows([ - {"name": "Sasha", "age": 42}, - {"name": "Hana"} - ]) - -asyncio.run(main()) -``` - -## Differences with `csv` - -`aiocsv` strives to be a drop-in replacement for Python's builtin -[csv module](https://docs.python.org/3/library/csv.html). However, there are 3 notable differences: - -- Readers accept objects with async `read` methods, instead of an AsyncIterable over lines - from a file. -- `AsyncDictReader.fieldnames` can be `None` - use `await AsyncDictReader.get_fieldnames()` instead. -- Changes to `csv.field_size_limit` are not picked up by existing Reader instances. - The field size limit is cached on Reader instantiation to avoid expensive function calls - on each character of the input. - -Other, minor, differences include: -- `AsyncReader.line_num`, `AsyncDictReader.line_num` and `AsyncDictReader.dialect` are not settable, -- `AsyncDictReader.reader` is of `AsyncReader` type, -- `AsyncDictWriter.writer` is of `AsyncWriter` type, -- `AsyncDictWriter` provides an extra, read-only `dialect` property. - - -## Reference - - -### aiocsv.AsyncReader - -``` -AsyncReader( - asyncfile: aiocsv.protocols.WithAsyncRead, - dialect: str | csv.Dialect | Type[csv.Dialect] = "excel", - **csv_dialect_kwargs: Unpack[aiocsv.protocols.CsvDialectKwargs], -) -``` - -An object that iterates over records in the given asynchronous CSV file. -Additional keyword arguments are understood as dialect parameters. - -Iterating over this object returns parsed CSV rows (`List[str]`). - -*Methods*: -- `__aiter__(self) -> self` -- `async __anext__(self) -> List[str]` - -*Read-only properties*: -- `dialect`: The csv.Dialect used when parsing -- `line_num`: The number of lines read from the source file. This coincides with a 1-based index - of the line number of the last line of the recently parsed record. - - -### aiocsv.AsyncDictReader - -``` -AsyncDictReader( - asyncfile: aiocsv.protocols.WithAsyncRead, - fieldnames: Optional[Sequence[str]] = None, - restkey: Optional[str] = None, - restval: Optional[str] = None, - dialect: str | csv.Dialect | Type[csv.Dialect] = "excel", - **csv_dialect_kwargs: Unpack[aiocsv.protocols.CsvDialectKwargs], -) -``` - -An object that iterates over records in the given asynchronous CSV file. -All arguments work exactly the same was as in csv.DictReader. - -Iterating over this object returns parsed CSV rows (`Dict[str, str]`). - -*Methods*: -- `__aiter__(self) -> self` -- `async __anext__(self) -> Dict[str, str]` -- `async get_fieldnames(self) -> List[str]` - - -*Properties*: -- `fieldnames`: field names used when converting rows to dictionaries - **⚠️** Unlike csv.DictReader, this property can't read the fieldnames if they are missing - - it's not possible to `await` on the header row in a property getter. - **Use `await reader.get_fieldnames()`**. - ```py - reader = csv.DictReader(some_file) - reader.fieldnames # ["cells", "from", "the", "header"] - - areader = aiofiles.AsyncDictReader(same_file_but_async) - areader.fieldnames # ⚠️ None - await areader.get_fieldnames() # ["cells", "from", "the", "header"] - ``` -- `restkey`: If a row has more cells then the header, all remaining cells are stored under - this key in the returned dictionary. Defaults to `None`. -- `restval`: If a row has less cells then the header, then missing keys will use this - value. Defaults to `None`. -- `reader`: Underlying `aiofiles.AsyncReader` instance - -*Read-only properties*: -- `dialect`: Link to `self.reader.dialect` - the current csv.Dialect -- `line_num`: The number of lines read from the source file. This coincides with a 1-based index - of the line number of the last line of the recently parsed record. - - -### aiocsv.AsyncWriter - -``` -AsyncWriter( - asyncfile: aiocsv.protocols.WithAsyncWrite, - dialect: str | csv.Dialect | Type[csv.Dialect] = "excel", - **csv_dialect_kwargs: Unpack[aiocsv.protocols.CsvDialectKwargs], -) -``` - -An object that writes csv rows to the given asynchronous file. -In this object "row" is a sequence of values. - -Additional keyword arguments are passed to the underlying csv.writer instance. - -*Methods*: -- `async writerow(self, row: Iterable[Any]) -> None`: - Writes one row to the specified file. -- `async writerows(self, rows: Iterable[Iterable[Any]]) -> None`: - Writes multiple rows to the specified file. - -*Readonly properties*: -- `dialect`: Link to underlying's csv.writer's `dialect` attribute - - -### aiocsv.AsyncDictWriter - -``` -AsyncDictWriter( - asyncfile: aiocsv.protocols.WithAsyncWrite, - fieldnames: Sequence[str], - restval: Any = "", - extrasaction: Literal["raise", "ignore"] = "raise", - dialect: str | csv.Dialect | Type[csv.Dialect] = "excel", - **csv_dialect_kwargs: Unpack[aiocsv.protocols.CsvDialectKwargs], -) -``` - -An object that writes csv rows to the given asynchronous file. -In this object "row" is a mapping from fieldnames to values. - -Additional keyword arguments are passed to the underlying csv.DictWriter instance. - -*Methods*: -- `async writeheader(self) -> None`: Writes header row to the specified file. -- `async writerow(self, row: Mapping[str, Any]) -> None`: - Writes one row to the specified file. -- `async writerows(self, rows: Iterable[Mapping[str, Any]]) -> None`: - Writes multiple rows to the specified file. - -*Properties*: -- `fieldnames`: Sequence of keys to identify the order of values when writing rows - to the underlying file -- `restval`: Placeholder value used when a key from fieldnames is missing in a row, - defaults to `""` -- `extrasaction`: Action to take when there are keys in a row, which are not present in - fieldnames, defaults to `"raise"` which causes ValueError to be raised on extra keys, - may be also set to `"ignore"` to ignore any extra keys -- `writer`: Link to the underlying `AsyncWriter` - -*Readonly properties*: -- `dialect`: Link to underlying's csv.reader's `dialect` attribute - - -### aiocsv.protocols.WithAsyncRead -A `typing.Protocol` describing an asynchronous file, which can be read. - - -### aiocsv.protocols.WithAsyncWrite -A `typing.Protocol` describing an asynchronous file, which can be written to. - - -### aiocsv.protocols.CsvDialectArg -Type of the `dialect` argument, as used in the `csv` module. - - -### aiocsv.protocols.CsvDialectKwargs -Keyword arguments used by `csv` module to override the dialect settings during reader/writer -instantiation. - -## Development - -Contributions are welcome, however please open an issue beforehand. `aiocsv` is meant as -a replacement for the built-in `csv`, any features not present in the latter will be rejected. - -### Building from source - -To create a wheel (and a source tarball), run `python -m build`. - -For local development, use a [virtual environment](https://docs.python.org/3/library/venv.html). -`pip install --editable .` will build the C extension and make it available for the current -venv. This is required for running the tests. However, [due to the mess of Python packaging](https://docs.python.org/3/library/venv.html) -this will force an optimized build without debugging symbols. If you need to debug the C part -of aiocsv and build the library with e.g. debugging symbols, the only sane way is to -run `python setup.py build --debug` and manually copy the shared object/DLL from `build/lib*/aiocsv` -to `aiocsv`. - -### Tests - -This project uses [pytest](https://docs.pytest.org/en/latest/contents.html) with -[pytest-asyncio](https://pypi.org/project/pytest-asyncio/) for testing. Run `pytest` -after installing the library in the manner explained above. - -### Linting & other tools - -This library uses [black](https://pypi.org/project/black/) and [isort](https://pypi.org/project/isort/) -for formatting and [pyright](https://github.com/microsoft/pyright) in strict mode for type checking. - -For the C part of library, please use [clang-format](https://clang.llvm.org/docs/ClangFormat.html) -for formatting and [clang-tidy](https://clang.llvm.org/extra/clang-tidy/) linting, -however this are not yet integrated in the CI. - -### Installing required tools - -`pip install -r requirements.dev.txt` will pull all of the development tools mentioned above, -however this might not be necessary depending on your setup. For example, if you use VS Code -with the Python extension, pyright is already bundled and doesn't need to be installed again. - -### Recommended VS Code settings - -Use [Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python), -[Pylance](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance) -(should be installed automatically alongside Python extension), -[black](https://marketplace.visualstudio.com/items?itemName=ms-python.black-formatter) and -[isort](https://marketplace.visualstudio.com/items?itemName=ms-python.isort) Python extensions. - -You will need to install all dev dependencies from `requirements.dev.txt`, except for `pyright`. -Recommended `.vscode/settings.json`: - -```json -{ - "C_Cpp.codeAnalysis.clangTidy.enabled": true, - "python.testing.pytestArgs": [ - "." - ], - "python.testing.unittestEnabled": false, - "python.testing.pytestEnabled": true, - "[python]": { - "editor.formatOnSave": true, - "editor.codeActionsOnSave": { - "source.organizeImports": "always" - } - }, - "[c]": { - "editor.formatOnSave": true - } -} -``` - -For the C part of the library, [C/C++ extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) is sufficient. -Ensure that your system has Python headers installed. Usually a separate package like python3-dev -needs to be installed, consult with your system repositories on that. `.vscode/c_cpp_properties.json` -needs to manually include Python headers under `includePath`. On my particular system this -config file looks like this: - -```json -{ - "configurations": [ - { - "name": "Linux", - "includePath": [ - "${workspaceFolder}/**", - "/usr/include/python3.11" - ], - "defines": [], - "compilerPath": "/usr/bin/clang", - "cStandard": "c17", - "cppStandard": "c++17", - "intelliSenseMode": "linux-clang-x64" - } - ], - "version": 4 -} -``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiocsv-1.3.2/aiocsv.egg-info/PKG-INFO new/aiocsv-1.3.2/aiocsv.egg-info/PKG-INFO --- old/aiocsv-1.3.2/aiocsv.egg-info/PKG-INFO 2024-04-28 12:30:03.000000000 +0200 +++ new/aiocsv-1.3.2/aiocsv.egg-info/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 @@ -1,356 +0,0 @@ -Metadata-Version: 2.1 -Name: aiocsv -Version: 1.3.2 -Author-email: Mikołaj Kuranowski <mkuranowski+pypacka...@gmail.com> -Project-URL: Homepage, https://github.com/MKuranowski/aiocsv -Keywords: async,asynchronous,csv,tsv -Classifier: Development Status :: 5 - Production/Stable -Classifier: License :: OSI Approved :: MIT License -Classifier: Framework :: AsyncIO -Classifier: Programming Language :: Python :: 3 :: Only -Requires-Python: >=3.8 -Description-Content-Type: text/markdown -License-File: LICENSE -Requires-Dist: typing_extensions - -# aiocsv - -Asynchronous CSV reading and writing. - - -## Installation - -`pip install aiocsv`. Python 3.8+ is required. - -This module contains an extension written in C. Pre-build binaries -may not be available for your configuration. You might need a C compiler -and Python headers to install aiocsv. - - -## Usage - -AsyncReader & AsyncDictReader accept any object that has a `read(size: int)` coroutine, -which should return a string. - -AsyncWriter & AsyncDictWriter accept any object that has a `write(b: str)` coroutine. - -Reading is implemented using a custom CSV parser, which should behave exactly like the CPython parser. - -Writing is implemented using the synchronous csv.writer and csv.DictWriter objects - -the serializers write data to a StringIO, and that buffer is then rewritten to the underlying -asynchronous file. - - -## Example - -Example usage with [aiofiles](https://pypi.org/project/aiofiles/). - -```python -import asyncio -import csv - -import aiofiles -from aiocsv import AsyncReader, AsyncDictReader, AsyncWriter, AsyncDictWriter - -async def main(): - # simple reading - async with aiofiles.open("some_file.csv", mode="r", encoding="utf-8", newline="") as afp: - async for row in AsyncReader(afp): - print(row) # row is a list - - # dict reading, tab-separated - async with aiofiles.open("some_other_file.tsv", mode="r", encoding="utf-8", newline="") as afp: - async for row in AsyncDictReader(afp, delimiter="\t"): - print(row) # row is a dict - - # simple writing, "unix"-dialect - async with aiofiles.open("new_file.csv", mode="w", encoding="utf-8", newline="") as afp: - writer = AsyncWriter(afp, dialect="unix") - await writer.writerow(["name", "age"]) - await writer.writerows([ - ["John", 26], ["Sasha", 42], ["Hana", 37] - ]) - - # dict writing, all quoted, "NULL" for missing fields - async with aiofiles.open("new_file2.csv", mode="w", encoding="utf-8", newline="") as afp: - writer = AsyncDictWriter(afp, ["name", "age"], restval="NULL", quoting=csv.QUOTE_ALL) - await writer.writeheader() - await writer.writerow({"name": "John", "age": 26}) - await writer.writerows([ - {"name": "Sasha", "age": 42}, - {"name": "Hana"} - ]) - -asyncio.run(main()) -``` - -## Differences with `csv` - -`aiocsv` strives to be a drop-in replacement for Python's builtin -[csv module](https://docs.python.org/3/library/csv.html). However, there are 3 notable differences: - -- Readers accept objects with async `read` methods, instead of an AsyncIterable over lines - from a file. -- `AsyncDictReader.fieldnames` can be `None` - use `await AsyncDictReader.get_fieldnames()` instead. -- Changes to `csv.field_size_limit` are not picked up by existing Reader instances. - The field size limit is cached on Reader instantiation to avoid expensive function calls - on each character of the input. - -Other, minor, differences include: -- `AsyncReader.line_num`, `AsyncDictReader.line_num` and `AsyncDictReader.dialect` are not settable, -- `AsyncDictReader.reader` is of `AsyncReader` type, -- `AsyncDictWriter.writer` is of `AsyncWriter` type, -- `AsyncDictWriter` provides an extra, read-only `dialect` property. - - -## Reference - - -### aiocsv.AsyncReader - -``` -AsyncReader( - asyncfile: aiocsv.protocols.WithAsyncRead, - dialect: str | csv.Dialect | Type[csv.Dialect] = "excel", - **csv_dialect_kwargs: Unpack[aiocsv.protocols.CsvDialectKwargs], -) -``` - -An object that iterates over records in the given asynchronous CSV file. -Additional keyword arguments are understood as dialect parameters. - -Iterating over this object returns parsed CSV rows (`List[str]`). - -*Methods*: -- `__aiter__(self) -> self` -- `async __anext__(self) -> List[str]` - -*Read-only properties*: -- `dialect`: The csv.Dialect used when parsing -- `line_num`: The number of lines read from the source file. This coincides with a 1-based index - of the line number of the last line of the recently parsed record. - - -### aiocsv.AsyncDictReader - -``` -AsyncDictReader( - asyncfile: aiocsv.protocols.WithAsyncRead, - fieldnames: Optional[Sequence[str]] = None, - restkey: Optional[str] = None, - restval: Optional[str] = None, - dialect: str | csv.Dialect | Type[csv.Dialect] = "excel", - **csv_dialect_kwargs: Unpack[aiocsv.protocols.CsvDialectKwargs], -) -``` - -An object that iterates over records in the given asynchronous CSV file. -All arguments work exactly the same was as in csv.DictReader. - -Iterating over this object returns parsed CSV rows (`Dict[str, str]`). - -*Methods*: -- `__aiter__(self) -> self` -- `async __anext__(self) -> Dict[str, str]` -- `async get_fieldnames(self) -> List[str]` - - -*Properties*: -- `fieldnames`: field names used when converting rows to dictionaries - **⚠️** Unlike csv.DictReader, this property can't read the fieldnames if they are missing - - it's not possible to `await` on the header row in a property getter. - **Use `await reader.get_fieldnames()`**. - ```py - reader = csv.DictReader(some_file) - reader.fieldnames # ["cells", "from", "the", "header"] - - areader = aiofiles.AsyncDictReader(same_file_but_async) - areader.fieldnames # ⚠️ None - await areader.get_fieldnames() # ["cells", "from", "the", "header"] - ``` -- `restkey`: If a row has more cells then the header, all remaining cells are stored under - this key in the returned dictionary. Defaults to `None`. -- `restval`: If a row has less cells then the header, then missing keys will use this - value. Defaults to `None`. -- `reader`: Underlying `aiofiles.AsyncReader` instance - -*Read-only properties*: -- `dialect`: Link to `self.reader.dialect` - the current csv.Dialect -- `line_num`: The number of lines read from the source file. This coincides with a 1-based index - of the line number of the last line of the recently parsed record. - - -### aiocsv.AsyncWriter - -``` -AsyncWriter( - asyncfile: aiocsv.protocols.WithAsyncWrite, - dialect: str | csv.Dialect | Type[csv.Dialect] = "excel", - **csv_dialect_kwargs: Unpack[aiocsv.protocols.CsvDialectKwargs], -) -``` - -An object that writes csv rows to the given asynchronous file. -In this object "row" is a sequence of values. - -Additional keyword arguments are passed to the underlying csv.writer instance. - -*Methods*: -- `async writerow(self, row: Iterable[Any]) -> None`: - Writes one row to the specified file. -- `async writerows(self, rows: Iterable[Iterable[Any]]) -> None`: - Writes multiple rows to the specified file. - -*Readonly properties*: -- `dialect`: Link to underlying's csv.writer's `dialect` attribute - - -### aiocsv.AsyncDictWriter - -``` -AsyncDictWriter( - asyncfile: aiocsv.protocols.WithAsyncWrite, - fieldnames: Sequence[str], - restval: Any = "", - extrasaction: Literal["raise", "ignore"] = "raise", - dialect: str | csv.Dialect | Type[csv.Dialect] = "excel", - **csv_dialect_kwargs: Unpack[aiocsv.protocols.CsvDialectKwargs], -) -``` - -An object that writes csv rows to the given asynchronous file. -In this object "row" is a mapping from fieldnames to values. - -Additional keyword arguments are passed to the underlying csv.DictWriter instance. - -*Methods*: -- `async writeheader(self) -> None`: Writes header row to the specified file. -- `async writerow(self, row: Mapping[str, Any]) -> None`: - Writes one row to the specified file. -- `async writerows(self, rows: Iterable[Mapping[str, Any]]) -> None`: - Writes multiple rows to the specified file. - -*Properties*: -- `fieldnames`: Sequence of keys to identify the order of values when writing rows - to the underlying file -- `restval`: Placeholder value used when a key from fieldnames is missing in a row, - defaults to `""` -- `extrasaction`: Action to take when there are keys in a row, which are not present in - fieldnames, defaults to `"raise"` which causes ValueError to be raised on extra keys, - may be also set to `"ignore"` to ignore any extra keys -- `writer`: Link to the underlying `AsyncWriter` - -*Readonly properties*: -- `dialect`: Link to underlying's csv.reader's `dialect` attribute - - -### aiocsv.protocols.WithAsyncRead -A `typing.Protocol` describing an asynchronous file, which can be read. - - -### aiocsv.protocols.WithAsyncWrite -A `typing.Protocol` describing an asynchronous file, which can be written to. - - -### aiocsv.protocols.CsvDialectArg -Type of the `dialect` argument, as used in the `csv` module. - - -### aiocsv.protocols.CsvDialectKwargs -Keyword arguments used by `csv` module to override the dialect settings during reader/writer -instantiation. - -## Development - -Contributions are welcome, however please open an issue beforehand. `aiocsv` is meant as -a replacement for the built-in `csv`, any features not present in the latter will be rejected. - -### Building from source - -To create a wheel (and a source tarball), run `python -m build`. - -For local development, use a [virtual environment](https://docs.python.org/3/library/venv.html). -`pip install --editable .` will build the C extension and make it available for the current -venv. This is required for running the tests. However, [due to the mess of Python packaging](https://docs.python.org/3/library/venv.html) -this will force an optimized build without debugging symbols. If you need to debug the C part -of aiocsv and build the library with e.g. debugging symbols, the only sane way is to -run `python setup.py build --debug` and manually copy the shared object/DLL from `build/lib*/aiocsv` -to `aiocsv`. - -### Tests - -This project uses [pytest](https://docs.pytest.org/en/latest/contents.html) with -[pytest-asyncio](https://pypi.org/project/pytest-asyncio/) for testing. Run `pytest` -after installing the library in the manner explained above. - -### Linting & other tools - -This library uses [black](https://pypi.org/project/black/) and [isort](https://pypi.org/project/isort/) -for formatting and [pyright](https://github.com/microsoft/pyright) in strict mode for type checking. - -For the C part of library, please use [clang-format](https://clang.llvm.org/docs/ClangFormat.html) -for formatting and [clang-tidy](https://clang.llvm.org/extra/clang-tidy/) linting, -however this are not yet integrated in the CI. - -### Installing required tools - -`pip install -r requirements.dev.txt` will pull all of the development tools mentioned above, -however this might not be necessary depending on your setup. For example, if you use VS Code -with the Python extension, pyright is already bundled and doesn't need to be installed again. - -### Recommended VS Code settings - -Use [Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python), -[Pylance](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance) -(should be installed automatically alongside Python extension), -[black](https://marketplace.visualstudio.com/items?itemName=ms-python.black-formatter) and -[isort](https://marketplace.visualstudio.com/items?itemName=ms-python.isort) Python extensions. - -You will need to install all dev dependencies from `requirements.dev.txt`, except for `pyright`. -Recommended `.vscode/settings.json`: - -```json -{ - "C_Cpp.codeAnalysis.clangTidy.enabled": true, - "python.testing.pytestArgs": [ - "." - ], - "python.testing.unittestEnabled": false, - "python.testing.pytestEnabled": true, - "[python]": { - "editor.formatOnSave": true, - "editor.codeActionsOnSave": { - "source.organizeImports": "always" - } - }, - "[c]": { - "editor.formatOnSave": true - } -} -``` - -For the C part of the library, [C/C++ extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) is sufficient. -Ensure that your system has Python headers installed. Usually a separate package like python3-dev -needs to be installed, consult with your system repositories on that. `.vscode/c_cpp_properties.json` -needs to manually include Python headers under `includePath`. On my particular system this -config file looks like this: - -```json -{ - "configurations": [ - { - "name": "Linux", - "includePath": [ - "${workspaceFolder}/**", - "/usr/include/python3.11" - ], - "defines": [], - "compilerPath": "/usr/bin/clang", - "cStandard": "c17", - "cppStandard": "c++17", - "intelliSenseMode": "linux-clang-x64" - } - ], - "version": 4 -} -``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiocsv-1.3.2/aiocsv.egg-info/SOURCES.txt new/aiocsv-1.3.2/aiocsv.egg-info/SOURCES.txt --- old/aiocsv-1.3.2/aiocsv.egg-info/SOURCES.txt 2024-04-28 12:30:03.000000000 +0200 +++ new/aiocsv-1.3.2/aiocsv.egg-info/SOURCES.txt 1970-01-01 01:00:00.000000000 +0100 @@ -1,23 +0,0 @@ -LICENSE -pyproject.toml -readme.md -setup.py -aiocsv/__init__.py -aiocsv/_parser.c -aiocsv/_parser.pyi -aiocsv/parser.py -aiocsv/protocols.py -aiocsv/py.typed -aiocsv/readers.py -aiocsv/writers.py -aiocsv.egg-info/PKG-INFO -aiocsv.egg-info/SOURCES.txt -aiocsv.egg-info/dependency_links.txt -aiocsv.egg-info/not-zip-safe -aiocsv.egg-info/requires.txt -aiocsv.egg-info/top_level.txt -tests/test_dialects.py -tests/test_dict.py -tests/test_newlines.py -tests/test_parser.py -tests/test_simple.py \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiocsv-1.3.2/aiocsv.egg-info/dependency_links.txt new/aiocsv-1.3.2/aiocsv.egg-info/dependency_links.txt --- old/aiocsv-1.3.2/aiocsv.egg-info/dependency_links.txt 2024-04-28 12:30:03.000000000 +0200 +++ new/aiocsv-1.3.2/aiocsv.egg-info/dependency_links.txt 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiocsv-1.3.2/aiocsv.egg-info/not-zip-safe new/aiocsv-1.3.2/aiocsv.egg-info/not-zip-safe --- old/aiocsv-1.3.2/aiocsv.egg-info/not-zip-safe 2024-02-21 18:56:51.000000000 +0100 +++ new/aiocsv-1.3.2/aiocsv.egg-info/not-zip-safe 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiocsv-1.3.2/aiocsv.egg-info/requires.txt new/aiocsv-1.3.2/aiocsv.egg-info/requires.txt --- old/aiocsv-1.3.2/aiocsv.egg-info/requires.txt 2024-04-28 12:30:03.000000000 +0200 +++ new/aiocsv-1.3.2/aiocsv.egg-info/requires.txt 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -typing_extensions diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiocsv-1.3.2/aiocsv.egg-info/top_level.txt new/aiocsv-1.3.2/aiocsv.egg-info/top_level.txt --- old/aiocsv-1.3.2/aiocsv.egg-info/top_level.txt 2024-04-28 12:30:03.000000000 +0200 +++ new/aiocsv-1.3.2/aiocsv.egg-info/top_level.txt 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -aiocsv diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiocsv-1.3.2/requirements.dev.txt new/aiocsv-1.3.2/requirements.dev.txt --- old/aiocsv-1.3.2/requirements.dev.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/aiocsv-1.3.2/requirements.dev.txt 2024-04-28 12:29:50.000000000 +0200 @@ -0,0 +1,9 @@ +-r requirements.txt +aiofiles +pytest +pytest-asyncio +setuptools +wheel +isort +black +pyright diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiocsv-1.3.2/requirements.txt new/aiocsv-1.3.2/requirements.txt --- old/aiocsv-1.3.2/requirements.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/aiocsv-1.3.2/requirements.txt 2024-04-28 12:29:50.000000000 +0200 @@ -0,0 +1 @@ +typing_extensions diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiocsv-1.3.2/setup.cfg new/aiocsv-1.3.2/setup.cfg --- old/aiocsv-1.3.2/setup.cfg 2024-04-28 12:30:03.977237000 +0200 +++ new/aiocsv-1.3.2/setup.cfg 1970-01-01 01:00:00.000000000 +0100 @@ -1,4 +0,0 @@ -[egg_info] -tag_build = -tag_date = 0 - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiocsv-1.3.2/tests/eu_cities_unix.csv new/aiocsv-1.3.2/tests/eu_cities_unix.csv --- old/aiocsv-1.3.2/tests/eu_cities_unix.csv 1970-01-01 01:00:00.000000000 +0100 +++ new/aiocsv-1.3.2/tests/eu_cities_unix.csv 2024-04-28 12:29:50.000000000 +0200 @@ -0,0 +1,5 @@ +"Berlin","Germany" +"Madrid","Spain" +"Rome","Italy" +"Bucharest","Romania" +"Paris","France" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiocsv-1.3.2/tests/math_constants.csv new/aiocsv-1.3.2/tests/math_constants.csv --- old/aiocsv-1.3.2/tests/math_constants.csv 1970-01-01 01:00:00.000000000 +0100 +++ new/aiocsv-1.3.2/tests/math_constants.csv 2024-04-28 12:29:50.000000000 +0200 @@ -0,0 +1,4 @@ +pi,3.1416 +sqrt2,1.4142 +phi,1.618 +e,2.7183 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiocsv-1.3.2/tests/metro_systems.tsv new/aiocsv-1.3.2/tests/metro_systems.tsv --- old/aiocsv-1.3.2/tests/metro_systems.tsv 1970-01-01 01:00:00.000000000 +0100 +++ new/aiocsv-1.3.2/tests/metro_systems.tsv 2024-04-28 12:29:50.000000000 +0200 @@ -0,0 +1,7 @@ +'City' 'Stations' 'System Length' +'New York' '424' '380' +'Shanghai' '345' '676' +'Seoul' '331' '353' +'Beijing' '326' '690' +'Paris' '302' '214' +'London' '270' '402' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiocsv-1.3.2/tests/newlines.csv new/aiocsv-1.3.2/tests/newlines.csv --- old/aiocsv-1.3.2/tests/newlines.csv 1970-01-01 01:00:00.000000000 +0100 +++ new/aiocsv-1.3.2/tests/newlines.csv 2024-04-28 12:29:50.000000000 +0200 @@ -0,0 +1,7 @@ +field1,field2,field3 +"hello","is it $"me""","you're +looking for" +this is going to be,another$ +broken row,"this time with escapechar" +"and now it's both quoted$ +and",with,"escape char"