diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml new file mode 100644 index 0000000..24308ce --- /dev/null +++ b/.github/workflows/python-package.yml @@ -0,0 +1,64 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python + +name: bintablefile-package-build + +on: + push: + branches: [ $default-branch ] + paths-ignore: + - '**.md' + pull_request: + branches: [ '**' ] + paths-ignore: + - '**.md' + + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + os: [ ubuntu-latest, windows-latest, macos-latest, ubuntu-24.04-arm ] + python-version: [ "3.10", "3.11","3.12", "3.13" ] + + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install flake8 pytest + pip install -e ".[dev]" + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + python -m unittest discover -s tests + + build-container: + runs-on: ubuntu-latest + container: + image: python:3.12-bullseye + steps: + - uses: actions/checkout@v4 + - name: Install dependencies in container + run: | + python -m pip install --upgrade pip + python -m pip install flake8 pytest + pip install -e ".[dev]" + - name: Lint with flake8 in container + run: | + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Run unittests in container + run: | + python -m unittest discover -s tests \ No newline at end of file diff --git a/README.md b/README.md index 5615844..8b23e55 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ # Binary Table File format +[![Build Status](https://github.com/eSAMTrade/bintablefile/actions/workflows/python-package.yml/badge.svg?branch=main)](https://github.com/eSAMTrade/bintablefile/actions/workflows/python-package.yml) + ## Binary Table File - efficient binary file format to store and retrieve tabular data @@ -21,4 +23,10 @@ pip install bintablefile The library can be found on [PyPi](https://pypi.org/project/bintablefile/): https://pypi.org/project/bintablefile/ -**Note**: We release directly v2.0 as v1.0 was used just internally and was not released to PyPI. The major improvement of V2 is a full-featured header, that allows to store metadata about the table, as well as store the number of records for ReadOnly compression formats like `idzip`. \ No newline at end of file +**Note**: We release directly v2.0 as v1.0 was used just internally and was not released to PyPI. The major improvement of V2 is a full-featured header, that allows to store metadata about the table, as well as store the number of records for ReadOnly compression formats like `idzip`. + + +### Local development build +```bash +pip install -e ".[dev]" +``` \ No newline at end of file diff --git a/requirements-dev.txt b/requirements-dev.txt index 28a2b71..6cc1cee 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,3 +1,5 @@ +-r requirements.txt + cython wheel twine diff --git a/setup.py b/setup.py index 21bfee1..d47dcc0 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,10 @@ class build(build_orig): def finalize_options(self): super().finalize_options() - __builtins__.__NUMPY_SETUP__ = False + try: + __builtins__.__NUMPY_SETUP__ = False + except AttributeError: + print("Numpy is not installed, skipping numpy include dir") import numpy for extension in self.distribution.ext_modules: extension.include_dirs.append(numpy.get_include()) @@ -33,6 +36,8 @@ def finalize_options(self): with open("requirements-dev.txt") as fp: dev_requires = fp.read().strip().split("\n") + # ignore the lines that starts with '#' or '-r ' + dev_requires = [line for line in dev_requires if line and not line.startswith(("#", "-r"))] setup( ext_modules=exts, diff --git a/tests/test_bintablefile.py b/tests/test_bintablefile.py index c5d4a50..fed742f 100644 --- a/tests/test_bintablefile.py +++ b/tests/test_bintablefile.py @@ -30,6 +30,52 @@ def setUp(self) -> None: def tearDown(self) -> None: self._tmp_dirpath.cleanup() + def test_structure(self): + import ctypes + + def create_structure(fields): + """ + Dynamically creates a ctypes.Structure subclass with given fields. + + :param fields: A list of tuples where each tuple contains the field name and ctype data type + :return: A new ctypes.Structure subclass with the specified fields + """ + + class DynamicStructure(ctypes.Structure): + _fields_ = fields + + return DynamicStructure + + def create_structure_array(struct_cls, n): + """ + Creates an array of ctypes.Structure of type struct_cls with n elements. + + :param struct_cls: The ctypes.Structure subclass + :param n: The number of elements in the array + :return: An instance of the ctypes.Array filled with structures of type struct_cls + """ + return (struct_cls * n)() + + # Example usage: + + # Define the list of field names and their ctypes types + fields = [ + ('id', ctypes.c_int), + ('value', ctypes.c_float), + ('name', ctypes.c_char * 20) # Example of a 20-char string + ] + + # Dynamically create the structure and array of structures + DynamicStruct = create_structure(fields) + StructArray = create_structure_array(DynamicStruct, 5) # Create an array of 5 elements + + # Example of how to use the array + StructArray[0].id = 1 + StructArray[0].value = 3.14 + StructArray[0].name = b"Example" + + print(StructArray[0].id, StructArray[0].value, StructArray[0].name) + def test_nominal_read_write_from_the_sameobject(self): record_format = (int, bool, float, Decimal) record_file = BinTableFile(self.fpath, record_format=record_format,