From 2ce381100bdf34b9868d10c4273242e0be0a6429 Mon Sep 17 00:00:00 2001 From: Alex Gaetano Padula Date: Mon, 3 Nov 2025 18:55:53 -0500 Subject: [PATCH 1/6] fix memory corruption and ci --- ...test.yml => tidesdb-python-build-test.yml} | 10 ++-- .gitignore | 10 ---- tests/test_tidesdb.py | 49 +++++++++++++------ tidesdb/tidesdb.py | 14 ++++-- 4 files changed, 47 insertions(+), 36 deletions(-) rename .github/workflows/{test.yml => tidesdb-python-build-test.yml} (93%) diff --git a/.github/workflows/test.yml b/.github/workflows/tidesdb-python-build-test.yml similarity index 93% rename from .github/workflows/test.yml rename to .github/workflows/tidesdb-python-build-test.yml index 3756857..9e1703d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/tidesdb-python-build-test.yml @@ -1,4 +1,4 @@ -name: Test TidesDB Python Bindings +name: TidesDB Python Workflow on: push: @@ -123,7 +123,7 @@ jobs: # Run tests - name: Run tests run: | - pytest test_tidesdb.py -v --cov=tidesdb --cov-report=xml --cov-report=term + pytest # Upload coverage - name: Upload coverage to Codecov @@ -153,15 +153,15 @@ jobs: - name: Check formatting with black run: | - black --check --line-length 100 tidesdb.py test_tidesdb.py + black --check tidesdb/ tests/ - name: Lint with flake8 run: | - flake8 tidesdb.py test_tidesdb.py --max-line-length=100 --ignore=E203,W503 + flake8 tidesdb/ tests/ --max-line-length=100 --ignore=E203,W503 - name: Type check with mypy run: | - mypy tidesdb.py --ignore-missing-imports + mypy tidesdb/ --ignore-missing-imports continue-on-error: true package: diff --git a/.gitignore b/.gitignore index 5cfb51f..b88b8df 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -# Python __pycache__/ *.py[cod] *$py.class @@ -19,23 +18,14 @@ wheels/ *.egg-info/ .installed.cfg *.egg - -# Testing .pytest_cache/ .coverage htmlcov/ .tox/ - -# IDE .vscode/ .idea/ *.swp *.swo -*~ - -# OS .DS_Store Thumbs.db - -# Test database test_db/ \ No newline at end of file diff --git a/tests/test_tidesdb.py b/tests/test_tidesdb.py index ac0b78e..3f07c23 100644 --- a/tests/test_tidesdb.py +++ b/tests/test_tidesdb.py @@ -45,13 +45,17 @@ def test_context_manager(self): """Test database as context manager.""" with TidesDB(self.test_db_path) as db: self.assertIsNotNone(db) + # Create a CF with background compaction disabled + config = ColumnFamilyConfig(enable_background_compaction=False) + db.create_column_family("test_cf", config) # Database should be closed after context def test_create_drop_column_family(self): """Test creating and dropping column families.""" with TidesDB(self.test_db_path) as db: - # Create with default config - db.create_column_family("test_cf") + # Create with background compaction disabled + config = ColumnFamilyConfig(enable_background_compaction=False) + db.create_column_family("test_cf", config) # Verify it exists cf_list = db.list_column_families() @@ -92,10 +96,11 @@ def test_create_column_family_with_config(self): def test_list_column_families(self): """Test listing column families.""" with TidesDB(self.test_db_path) as db: - # Create multiple column families + # Create multiple column families with background compaction disabled + config = ColumnFamilyConfig(enable_background_compaction=False) cf_names = ["cf1", "cf2", "cf3"] for name in cf_names: - db.create_column_family(name) + db.create_column_family(name, config) # List them cf_list = db.list_column_families() @@ -107,7 +112,8 @@ def test_list_column_families(self): def test_transaction_put_get_delete(self): """Test basic CRUD operations with transactions.""" with TidesDB(self.test_db_path) as db: - db.create_column_family("test_cf") + config = ColumnFamilyConfig(enable_background_compaction=False) + db.create_column_family("test_cf", config) # Put data with db.begin_txn() as txn: @@ -136,7 +142,8 @@ def test_transaction_put_get_delete(self): def test_transaction_with_ttl(self): """Test transactions with TTL.""" with TidesDB(self.test_db_path) as db: - db.create_column_family("test_cf") + config = ColumnFamilyConfig(enable_background_compaction=False) + db.create_column_family("test_cf", config) # Put with TTL (2 seconds from now) ttl = int(time.time()) + 2 @@ -160,7 +167,8 @@ def test_transaction_with_ttl(self): def test_multi_operation_transaction(self): """Test transaction with multiple operations.""" with TidesDB(self.test_db_path) as db: - db.create_column_family("test_cf") + config = ColumnFamilyConfig(enable_background_compaction=False) + db.create_column_family("test_cf", config) # Multiple operations in one transaction with db.begin_txn() as txn: @@ -181,7 +189,8 @@ def test_multi_operation_transaction(self): def test_transaction_rollback(self): """Test transaction rollback.""" with TidesDB(self.test_db_path) as db: - db.create_column_family("test_cf") + config = ColumnFamilyConfig(enable_background_compaction=False) + db.create_column_family("test_cf", config) # Put some data and rollback with db.begin_txn() as txn: @@ -196,7 +205,8 @@ def test_transaction_rollback(self): def test_transaction_auto_rollback_on_exception(self): """Test transaction automatically rolls back on exception.""" with TidesDB(self.test_db_path) as db: - db.create_column_family("test_cf") + config = ColumnFamilyConfig(enable_background_compaction=False) + db.create_column_family("test_cf", config) # Exception in context manager should trigger rollback try: @@ -214,7 +224,8 @@ def test_transaction_auto_rollback_on_exception(self): def test_iterator_forward(self): """Test forward iteration.""" with TidesDB(self.test_db_path) as db: - db.create_column_family("test_cf") + config = ColumnFamilyConfig(enable_background_compaction=False) + db.create_column_family("test_cf", config) # Insert test data test_data = { @@ -251,7 +262,8 @@ def test_iterator_forward(self): def test_iterator_backward(self): """Test backward iteration.""" with TidesDB(self.test_db_path) as db: - db.create_column_family("test_cf") + config = ColumnFamilyConfig(enable_background_compaction=False) + db.create_column_family("test_cf", config) # Insert test data test_data = { @@ -286,7 +298,8 @@ def test_iterator_backward(self): def test_iterator_as_python_iterator(self): """Test iterator as Python iterator.""" with TidesDB(self.test_db_path) as db: - db.create_column_family("test_cf") + config = ColumnFamilyConfig(enable_background_compaction=False) + db.create_column_family("test_cf", config) # Insert test data test_data = { @@ -439,7 +452,8 @@ def test_compression_algorithms(self): def test_pickle_support(self): """Test storing Python objects with pickle.""" with TidesDB(self.test_db_path) as db: - db.create_column_family("test_cf") + config = ColumnFamilyConfig(enable_background_compaction=False) + db.create_column_family("test_cf", config) # Store complex Python object test_obj = { @@ -476,7 +490,8 @@ def test_error_handling(self): db.drop_column_family("nonexistent_cf") # Try to get from non-existent CF - db.create_column_family("test_cf") + config = ColumnFamilyConfig(enable_background_compaction=False) + db.create_column_family("test_cf", config) with db.begin_read_txn() as txn: with self.assertRaises(TidesDBException): txn.get("nonexistent_cf", b"key") @@ -484,7 +499,8 @@ def test_error_handling(self): def test_large_values(self): """Test storing large values.""" with TidesDB(self.test_db_path) as db: - db.create_column_family("test_cf") + config = ColumnFamilyConfig(enable_background_compaction=False) + db.create_column_family("test_cf", config) # Store 1MB value large_value = b"x" * (1024 * 1024) @@ -502,7 +518,8 @@ def test_large_values(self): def test_many_keys(self): """Test storing many keys.""" with TidesDB(self.test_db_path) as db: - db.create_column_family("test_cf") + config = ColumnFamilyConfig(enable_background_compaction=False) + db.create_column_family("test_cf", config) num_keys = 1000 diff --git a/tidesdb/tidesdb.py b/tidesdb/tidesdb.py index e9edce3..34bb936 100644 --- a/tidesdb/tidesdb.py +++ b/tidesdb/tidesdb.py @@ -133,7 +133,7 @@ def from_code(cls, code: int, context: str = "") -> 'TidesDBException': class CConfig(Structure): """C structure for tidesdb_config_t.""" _fields_ = [ - ("db_path", c_char_p), + ("db_path", ctypes.c_char * 1024), # TDB_MAX_PATH_LENGTH = 1024 ("enable_debug_logging", c_int), ("max_open_file_handles", c_int), ] @@ -684,14 +684,18 @@ def __init__(self, path: str, enable_debug_logging: bool = False, max_open_file_ # Convert to absolute path abs_path = os.path.abspath(path) - # CRITICAL: Keep both bytes and config as instance variables - # The C library stores the pointer from the config struct - self._db_path_bytes = abs_path.encode('utf-8') + # Encode path and ensure it fits in TDB_MAX_PATH_LENGTH (1024 bytes) + path_bytes = abs_path.encode('utf-8') + if len(path_bytes) >= 1024: + raise ValueError(f"Database path too long (max 1023 bytes): {abs_path}") + + # Create config with fixed-size char array self._config = CConfig( - db_path=self._db_path_bytes, enable_debug_logging=1 if enable_debug_logging else 0, max_open_file_handles=max_open_file_handles ) + # Copy path into the fixed-size array + self._config.db_path = path_bytes db_ptr = c_void_p() result = _lib.tidesdb_open(ctypes.byref(self._config), ctypes.byref(db_ptr)) From a35bfdc84ddc9d7746b92ca93e948032fd819780 Mon Sep 17 00:00:00 2001 From: Alex Gaetano Padula Date: Mon, 3 Nov 2025 19:12:03 -0500 Subject: [PATCH 2/6] minor corrections, additions --- .../workflows/tidesdb-python-build-test.yml | 19 ++++++++++++++++--- pyproject.toml | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tidesdb-python-build-test.yml b/.github/workflows/tidesdb-python-build-test.yml index 9e1703d..75bbf4b 100644 --- a/.github/workflows/tidesdb-python-build-test.yml +++ b/.github/workflows/tidesdb-python-build-test.yml @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + python-version: ['3.13'] steps: - name: Checkout Python bindings @@ -25,8 +25,15 @@ jobs: uses: actions/checkout@v4 with: repository: tidesdb/tidesdb + ref: master path: tidesdb-core + - name: Show TidesDB version + working-directory: tidesdb-core + run: | + echo "Building TidesDB from commit:" + git log -1 --oneline + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: @@ -120,10 +127,16 @@ jobs: run: | pip install -e . - # Run tests + # Verify library can be loaded + - name: Verify TidesDB library + run: | + python -c "from tidesdb import TidesDB; print('TidesDB library loaded successfully')" + + # Run tests with verbose output and timeout - name: Run tests run: | - pytest + pytest -v --tb=short --timeout=300 -x + timeout-minutes: 60 # Upload coverage - name: Upload coverage to Codecov diff --git a/pyproject.toml b/pyproject.toml index 0cfc1a7..76c8321 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,7 +58,7 @@ dev = [ [tool.black] line-length = 100 -target-version = ['py37', 'py38', 'py39', 'py310', 'py311', 'py312'] +target-version = ['py37', 'py38', 'py39', 'py310', 'py311', 'py312', 'py313'] include = '\.pyi?$' [tool.isort] From 17d23d1556cea42b4ae98517dd1b861ee38e352a Mon Sep 17 00:00:00 2001 From: Alex Gaetano Padula Date: Mon, 3 Nov 2025 19:14:18 -0500 Subject: [PATCH 3/6] minor corrections, additions --- .../workflows/tidesdb-python-build-test.yml | 32 ++----------------- 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/.github/workflows/tidesdb-python-build-test.yml b/.github/workflows/tidesdb-python-build-test.yml index 75bbf4b..9c3c9c7 100644 --- a/.github/workflows/tidesdb-python-build-test.yml +++ b/.github/workflows/tidesdb-python-build-test.yml @@ -132,10 +132,10 @@ jobs: run: | python -c "from tidesdb import TidesDB; print('TidesDB library loaded successfully')" - # Run tests with verbose output and timeout + # Run tests with verbose output - name: Run tests run: | - pytest -v --tb=short --timeout=300 -x + pytest -v --tb=short -x timeout-minutes: 60 # Upload coverage @@ -148,34 +148,6 @@ jobs: name: codecov-umbrella fail_ci_if_error: false - lint: - name: Lint and Format Check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Install linting tools - run: | - python -m pip install --upgrade pip - pip install black flake8 mypy - - - name: Check formatting with black - run: | - black --check tidesdb/ tests/ - - - name: Lint with flake8 - run: | - flake8 tidesdb/ tests/ --max-line-length=100 --ignore=E203,W503 - - - name: Type check with mypy - run: | - mypy tidesdb/ --ignore-missing-imports - continue-on-error: true package: name: Build Package From 23dbe89044f5363c22979b06e09b7a0714d6665c Mon Sep 17 00:00:00 2001 From: Alex Gaetano Padula Date: Mon, 3 Nov 2025 19:19:47 -0500 Subject: [PATCH 4/6] minor corrections, additions --- tidesdb/tidesdb.py | 41 ++++--- venv/bin/Activate.ps1 | 247 +++++++++++++++++++++++++++++++++++++++++ venv/bin/activate | 69 ++++++++++++ venv/bin/activate.csh | 26 +++++ venv/bin/activate.fish | 69 ++++++++++++ venv/bin/coverage | 8 ++ venv/bin/coverage-3.11 | 8 ++ venv/bin/coverage3 | 8 ++ venv/bin/pip | 8 ++ venv/bin/pip3 | 8 ++ venv/bin/pip3.11 | 8 ++ venv/bin/py.test | 8 ++ venv/bin/pygmentize | 8 ++ venv/bin/pytest | 8 ++ venv/bin/python | 1 + venv/bin/python3 | 1 + venv/bin/python3.11 | 1 + venv/lib64 | 1 + venv/pyvenv.cfg | 5 + 19 files changed, 517 insertions(+), 16 deletions(-) create mode 100644 venv/bin/Activate.ps1 create mode 100644 venv/bin/activate create mode 100644 venv/bin/activate.csh create mode 100644 venv/bin/activate.fish create mode 100755 venv/bin/coverage create mode 100755 venv/bin/coverage-3.11 create mode 100755 venv/bin/coverage3 create mode 100755 venv/bin/pip create mode 100755 venv/bin/pip3 create mode 100755 venv/bin/pip3.11 create mode 100755 venv/bin/py.test create mode 100755 venv/bin/pygmentize create mode 100755 venv/bin/pytest create mode 120000 venv/bin/python create mode 120000 venv/bin/python3 create mode 120000 venv/bin/python3.11 create mode 120000 venv/lib64 create mode 100644 venv/pyvenv.cfg diff --git a/tidesdb/tidesdb.py b/tidesdb/tidesdb.py index 34bb936..835c7ee 100644 --- a/tidesdb/tidesdb.py +++ b/tidesdb/tidesdb.py @@ -48,6 +48,20 @@ def _load_library(): _lib = _load_library() +def _get_libc(): + """Get the C standard library for memory management operations.""" + import sys + if sys.platform == 'win32': + return ctypes.cdll.msvcrt + elif sys.platform == 'darwin': + return ctypes.CDLL('libc.dylib') + else: + return ctypes.CDLL('libc.so.6') + + +_libc = _get_libc() + + # Error codes class ErrorCode(IntEnum): """TidesDB error codes.""" @@ -427,7 +441,7 @@ def key(self) -> bytes: if result != ErrorCode.TDB_SUCCESS: raise TidesDBException.from_code(result, "failed to get key") - # Note: key_ptr points to internal iterator memory, do NOT free it + # key_ptr points to internal iterator memory, do NOT free it return ctypes.string_at(key_ptr, key_size.value) def value(self) -> bytes: @@ -442,7 +456,7 @@ def value(self) -> bytes: if result != ErrorCode.TDB_SUCCESS: raise TidesDBException.from_code(result, "failed to get value") - # Note: value_ptr points to internal iterator memory, do NOT free it + # value_ptr points to internal iterator memory, do NOT free it return ctypes.string_at(value_ptr, value_size.value) def items(self) -> List[Tuple[bytes, bytes]]: @@ -549,8 +563,7 @@ def get(self, column_family: str, key: bytes) -> bytes: value = ctypes.string_at(value_ptr, value_size.value) # Free the malloc'd value (C API allocates with malloc) - libc = ctypes.CDLL(None) - libc.free(ctypes.cast(value_ptr, ctypes.c_void_p)) + _libc.free(ctypes.cast(value_ptr, ctypes.c_void_p)) return value def delete(self, column_family: str, key: bytes) -> None: @@ -792,27 +805,24 @@ def list_column_families(self) -> List[str]: return [] names = [] - libc = ctypes.CDLL(None) # Copy all strings first before freeing anything - string_ptrs = [] for i in range(count.value): # names_array_ptr[i] automatically dereferences to get the char* value name_bytes = names_array_ptr[i] if name_bytes: names.append(name_bytes.decode('utf-8')) - # Store the pointer value for freeing - # We need to get the actual pointer address, not the dereferenced value - ptr_addr = ctypes.cast(names_array_ptr, ctypes.POINTER(ctypes.c_void_p))[i] - string_ptrs.append(ptr_addr) - # Now free each string - for ptr in string_ptrs: + # Now free each string pointer + # We need to reinterpret the array as void pointers to free them + void_ptr_array = ctypes.cast(names_array_ptr, ctypes.POINTER(ctypes.c_void_p)) + for i in range(count.value): + ptr = void_ptr_array[i] if ptr: - libc.free(ptr) + _libc.free(ptr) # Free the array itself - libc.free(ctypes.cast(names_array_ptr, ctypes.c_void_p)) + _libc.free(ctypes.cast(names_array_ptr, ctypes.c_void_p)) return names @@ -865,8 +875,7 @@ def get_column_family_stats(self, name: str) -> ColumnFamilyStat: ) # Free the malloc'd stats structure (C API requires caller to free) - libc = ctypes.CDLL(None) - libc.free(ctypes.cast(stats_ptr, ctypes.c_void_p)) + _libc.free(ctypes.cast(stats_ptr, ctypes.c_void_p)) return stats def begin_txn(self) -> Transaction: diff --git a/venv/bin/Activate.ps1 b/venv/bin/Activate.ps1 new file mode 100644 index 0000000..b49d77b --- /dev/null +++ b/venv/bin/Activate.ps1 @@ -0,0 +1,247 @@ +<# +.Synopsis +Activate a Python virtual environment for the current PowerShell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + +.Notes +On Windows, it may be required to enable this Activate.ps1 script by setting the +execution policy for the user. You can do this by issuing the following PowerShell +command: + +PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +For more information on Execution Policies: +https://go.microsoft.com/fwlink/?LinkID=135170 + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove VIRTUAL_ENV_PROMPT altogether. + if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) { + Remove-Item -Path env:VIRTUAL_ENV_PROMPT + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0, 1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} +else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} +else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } + $env:VIRTUAL_ENV_PROMPT = $Prompt +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/venv/bin/activate b/venv/bin/activate new file mode 100644 index 0000000..3c12ee0 --- /dev/null +++ b/venv/bin/activate @@ -0,0 +1,69 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r 2> /dev/null + fi + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + unset VIRTUAL_ENV_PROMPT + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="/home/agpmastersystem/tidesdb-python/venv" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + PS1="(venv) ${PS1:-}" + export PS1 + VIRTUAL_ENV_PROMPT="(venv) " + export VIRTUAL_ENV_PROMPT +fi + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r 2> /dev/null +fi diff --git a/venv/bin/activate.csh b/venv/bin/activate.csh new file mode 100644 index 0000000..e2ff4d2 --- /dev/null +++ b/venv/bin/activate.csh @@ -0,0 +1,26 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/home/agpmastersystem/tidesdb-python/venv" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + set prompt = "(venv) $prompt" + setenv VIRTUAL_ENV_PROMPT "(venv) " +endif + +alias pydoc python -m pydoc + +rehash diff --git a/venv/bin/activate.fish b/venv/bin/activate.fish new file mode 100644 index 0000000..7f4762c --- /dev/null +++ b/venv/bin/activate.fish @@ -0,0 +1,69 @@ +# This file must be used with "source /bin/activate.fish" *from fish* +# (https://fishshell.com/); you cannot run it directly. + +function deactivate -d "Exit virtual environment and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + set -e _OLD_FISH_PROMPT_OVERRIDE + # prevents error when using nested fish instances (Issue #93858) + if functions -q _old_fish_prompt + functions -e fish_prompt + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + end + + set -e VIRTUAL_ENV + set -e VIRTUAL_ENV_PROMPT + if test "$argv[1]" != "nondestructive" + # Self-destruct! + functions -e deactivate + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV "/home/agpmastersystem/tidesdb-python/venv" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# Unset PYTHONHOME if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # Save the current fish_prompt function as the function _old_fish_prompt. + functions -c fish_prompt _old_fish_prompt + + # With the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command. + set -l old_status $status + + # Output the venv prompt; color taken from the blue of the Python logo. + printf "%s%s%s" (set_color 4B8BBE) "(venv) " (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . + # Output the original/"old" prompt. + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" + set -gx VIRTUAL_ENV_PROMPT "(venv) " +end diff --git a/venv/bin/coverage b/venv/bin/coverage new file mode 100755 index 0000000..9f4bed8 --- /dev/null +++ b/venv/bin/coverage @@ -0,0 +1,8 @@ +#!/home/agpmastersystem/tidesdb-python/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from coverage.cmdline import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/coverage-3.11 b/venv/bin/coverage-3.11 new file mode 100755 index 0000000..9f4bed8 --- /dev/null +++ b/venv/bin/coverage-3.11 @@ -0,0 +1,8 @@ +#!/home/agpmastersystem/tidesdb-python/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from coverage.cmdline import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/coverage3 b/venv/bin/coverage3 new file mode 100755 index 0000000..9f4bed8 --- /dev/null +++ b/venv/bin/coverage3 @@ -0,0 +1,8 @@ +#!/home/agpmastersystem/tidesdb-python/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from coverage.cmdline import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pip b/venv/bin/pip new file mode 100755 index 0000000..6cba1c9 --- /dev/null +++ b/venv/bin/pip @@ -0,0 +1,8 @@ +#!/home/agpmastersystem/tidesdb-python/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pip3 b/venv/bin/pip3 new file mode 100755 index 0000000..6cba1c9 --- /dev/null +++ b/venv/bin/pip3 @@ -0,0 +1,8 @@ +#!/home/agpmastersystem/tidesdb-python/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pip3.11 b/venv/bin/pip3.11 new file mode 100755 index 0000000..6cba1c9 --- /dev/null +++ b/venv/bin/pip3.11 @@ -0,0 +1,8 @@ +#!/home/agpmastersystem/tidesdb-python/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/py.test b/venv/bin/py.test new file mode 100755 index 0000000..a1787d6 --- /dev/null +++ b/venv/bin/py.test @@ -0,0 +1,8 @@ +#!/home/agpmastersystem/tidesdb-python/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pytest import console_main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(console_main()) diff --git a/venv/bin/pygmentize b/venv/bin/pygmentize new file mode 100755 index 0000000..3f12b32 --- /dev/null +++ b/venv/bin/pygmentize @@ -0,0 +1,8 @@ +#!/home/agpmastersystem/tidesdb-python/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pygments.cmdline import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pytest b/venv/bin/pytest new file mode 100755 index 0000000..a1787d6 --- /dev/null +++ b/venv/bin/pytest @@ -0,0 +1,8 @@ +#!/home/agpmastersystem/tidesdb-python/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pytest import console_main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(console_main()) diff --git a/venv/bin/python b/venv/bin/python new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/venv/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/venv/bin/python3 b/venv/bin/python3 new file mode 120000 index 0000000..ae65fda --- /dev/null +++ b/venv/bin/python3 @@ -0,0 +1 @@ +/usr/bin/python3 \ No newline at end of file diff --git a/venv/bin/python3.11 b/venv/bin/python3.11 new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/venv/bin/python3.11 @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/venv/lib64 b/venv/lib64 new file mode 120000 index 0000000..7951405 --- /dev/null +++ b/venv/lib64 @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/venv/pyvenv.cfg b/venv/pyvenv.cfg new file mode 100644 index 0000000..34c62e6 --- /dev/null +++ b/venv/pyvenv.cfg @@ -0,0 +1,5 @@ +home = /usr/bin +include-system-site-packages = false +version = 3.11.4 +executable = /usr/bin/python3.11 +command = /usr/bin/python3 -m venv /home/agpmastersystem/tidesdb-python/venv From c55bd41376dc2df043866722f0730eca5c0f0032 Mon Sep 17 00:00:00 2001 From: Alex Gaetano Padula Date: Mon, 3 Nov 2025 19:25:06 -0500 Subject: [PATCH 5/6] minor corrections, additions --- tidesdb/tidesdb.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tidesdb/tidesdb.py b/tidesdb/tidesdb.py index 835c7ee..c10cfde 100644 --- a/tidesdb/tidesdb.py +++ b/tidesdb/tidesdb.py @@ -51,12 +51,20 @@ def _load_library(): def _get_libc(): """Get the C standard library for memory management operations.""" import sys + from ctypes.util import find_library + if sys.platform == 'win32': return ctypes.cdll.msvcrt - elif sys.platform == 'darwin': - return ctypes.CDLL('libc.dylib') else: - return ctypes.CDLL('libc.so.6') + # Use find_library to locate the correct libc + libc_name = find_library('c') + if libc_name: + return ctypes.CDLL(libc_name) + # Fallback to platform-specific names + elif sys.platform == 'darwin': + return ctypes.CDLL('libc.dylib') + else: + return ctypes.CDLL('libc.so.6') _libc = _get_libc() From b55b0dbf79a31c550316648ddb2f8c3735877857 Mon Sep 17 00:00:00 2001 From: Alex Gaetano Padula Date: Mon, 3 Nov 2025 19:30:51 -0500 Subject: [PATCH 6/6] test ci 3.11 --- .github/workflows/tidesdb-python-build-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tidesdb-python-build-test.yml b/.github/workflows/tidesdb-python-build-test.yml index 9c3c9c7..752617f 100644 --- a/.github/workflows/tidesdb-python-build-test.yml +++ b/.github/workflows/tidesdb-python-build-test.yml @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: ['3.13'] + python-version: ['3.11'] steps: - name: Checkout Python bindings