acstore
Advanced tools
| # Read the Docs configuration file for Sphinx projects | ||
| # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details | ||
| version: 2 | ||
| build: | ||
| os: ubuntu-22.04 | ||
| tools: | ||
| python: "3.12" | ||
| sphinx: | ||
| configuration: docs/conf.py | ||
| fail_on_warning: false | ||
| python: | ||
| install: | ||
| - requirements: docs/requirements.txt |
| # -*- coding: utf-8 -*- | ||
| """Attribute container JSON serializer.""" | ||
| from acstore.containers import manager as containers_manager | ||
| from acstore.helpers import schema as schema_helper | ||
| class AttributeContainerJSONSerializer(object): | ||
| """Attribute container JSON serializer.""" | ||
| _CONTAINERS_MANAGER = containers_manager.AttributeContainersManager | ||
| @classmethod | ||
| def ConvertAttributeContainerToJSON(cls, attribute_container): | ||
| """Converts an attribute container object into a JSON dictioary. | ||
| The resulting dictionary of the JSON serialized objects consists of: | ||
| { | ||
| '__type__': 'AttributeContainer' | ||
| '__container_type__': ... | ||
| ... | ||
| } | ||
| Here '__type__' indicates the object base type. In this case | ||
| 'AttributeContainer'. | ||
| '__container_type__' indicates the container type and rest of the elements | ||
| of the dictionary that make up the attributes of the container. | ||
| Args: | ||
| attribute_container (AttributeContainer): attribute container. | ||
| Returns: | ||
| dict[str, object]: JSON serialized objects. | ||
| """ | ||
| try: | ||
| schema = cls._CONTAINERS_MANAGER.GetSchema( | ||
| attribute_container.CONTAINER_TYPE) | ||
| except ValueError: | ||
| schema = {} | ||
| json_dict = { | ||
| '__type__': 'AttributeContainer', | ||
| '__container_type__': attribute_container.CONTAINER_TYPE} | ||
| for attribute_name, attribute_value in attribute_container.GetAttributes(): | ||
| data_type = schema.get(attribute_name, None) | ||
| serializer = schema_helper.SchemaHelper.GetAttributeSerializer( | ||
| data_type, 'json') | ||
| if serializer: | ||
| attribute_value = serializer.SerializeValue(attribute_value) | ||
| # JSON will not serialize certain runtime types like set, therefore | ||
| # these are cast to list first. | ||
| if isinstance(attribute_value, set): | ||
| attribute_value = list(attribute_value) | ||
| json_dict[attribute_name] = attribute_value | ||
| return json_dict | ||
| @classmethod | ||
| def ConvertJSONToAttributeContainer(cls, json_dict): | ||
| """Converts a JSON dictionary into an attribute container object. | ||
| The dictionary of the JSON serialized objects consists of: | ||
| { | ||
| '__type__': 'AttributeContainer' | ||
| '__container_type__': ... | ||
| ... | ||
| } | ||
| Here '__type__' indicates the object base type. In this case | ||
| 'AttributeContainer'. | ||
| '__container_type__' indicates the container type and rest of the elements | ||
| of the dictionary that make up the attributes of the container. | ||
| Args: | ||
| json_dict (dict[str, object]): JSON serialized objects. | ||
| Returns: | ||
| AttributeContainer: attribute container. | ||
| """ | ||
| # Use __container_type__ to indicate the attribute container type. | ||
| container_type = json_dict.get('__container_type__', None) | ||
| attribute_container = cls._CONTAINERS_MANAGER.CreateAttributeContainer( | ||
| container_type) | ||
| supported_attribute_names = attribute_container.GetAttributeNames() | ||
| for attribute_name, attribute_value in json_dict.items(): | ||
| if attribute_name in ('__container_type__', '__type__'): | ||
| continue | ||
| # Be strict about which attributes to set. | ||
| if attribute_name not in supported_attribute_names: | ||
| continue | ||
| setattr(attribute_container, attribute_name, attribute_value) | ||
| return attribute_container |
| [build-system] | ||
| requires = ["setuptools", "wheel"] | ||
| build-backend = "setuptools.build_meta" | ||
| [tool.docformatter] | ||
| black = false | ||
| non-cap = ["dfDateTime", "dfImageTools", "dfVFS", "dfWinReg", "dtFabric"] | ||
| non-strict = false | ||
| wrap-summaries = 80 | ||
| wrap-descriptions = 80 |
| #!/usr/bin/env python3 | ||
| # -*- coding: utf-8 -*- | ||
| """Tests for the attribute container JSON serializer.""" | ||
| import unittest | ||
| from acstore.containers import manager | ||
| from acstore.helpers import json_serializer | ||
| from tests import test_lib as shared_test_lib | ||
| class AttributeContainerJSONSerializerTest(shared_test_lib.BaseTestCase): | ||
| """Tests for the attribute container JSON serializer.""" | ||
| _TEST_MANAGER = manager.AttributeContainersManager | ||
| _TEST_SERIALIZER = json_serializer.AttributeContainerJSONSerializer | ||
| def testConvertAttributeContainerToJSON(self): | ||
| """Tests the ConvertAttributeContainerToJSON function.""" | ||
| attribute_container = shared_test_lib.TestAttributeContainer() | ||
| attribute_container.attribute = 'MyAttribute' | ||
| expected_json_dict = { | ||
| '__container_type__': 'test_container', | ||
| '__type__': 'AttributeContainer', | ||
| 'attribute': 'MyAttribute'} | ||
| json_dict = self._TEST_SERIALIZER.ConvertAttributeContainerToJSON( | ||
| attribute_container) | ||
| self.assertEqual(json_dict, expected_json_dict) | ||
| def testConvertJSONToAttributeContainer(self): | ||
| """Tests the ConvertJSONToAttributeContainer function.""" | ||
| json_dict = { | ||
| '__container_type__': 'test_container', | ||
| '__type__': 'AttributeContainer', | ||
| 'attribute': 'MyAttribute'} | ||
| self._TEST_MANAGER.RegisterAttributeContainer( | ||
| shared_test_lib.TestAttributeContainer) | ||
| try: | ||
| attribute_container = ( | ||
| self._TEST_SERIALIZER.ConvertJSONToAttributeContainer(json_dict)) | ||
| finally: | ||
| self._TEST_MANAGER.DeregisterAttributeContainer( | ||
| shared_test_lib.TestAttributeContainer) | ||
| self.assertIsNotNone(attribute_container) | ||
| self.assertEqual(attribute_container.CONTAINER_TYPE, 'test_container') | ||
| self.assertEqual(attribute_container.attribute, 'MyAttribute') | ||
| if __name__ == '__main__': | ||
| unittest.main() |
@@ -61,3 +61,3 @@ # Run tests on Fedora and Ubuntu Docker images using GIFT CORP and GIFT PPA on commit | ||
| apt-get update -q | ||
| apt-get install -y build-essential python3 python3-dev python3-distutils python3-setuptools python3-yaml | ||
| apt-get install -y build-essential python3 python3-dev python3-distutils python3-pip python3-setuptools python3-wheel python3-yaml | ||
| - name: Run tests | ||
@@ -73,2 +73,5 @@ env: | ||
| if test -f tests/end-to-end.py; then PYTHONPATH=. python3 ./tests/end-to-end.py --debug -c config/end-to-end.ini; fi | ||
| - name: Update setuptools | ||
| run: | | ||
| python3 -m pip install -U setuptools | ||
| - name: Build source distribution | ||
@@ -75,0 +78,0 @@ run: | |
@@ -17,3 +17,3 @@ # Run docs tox tests on Ubuntu Docker images using GIFT PPA | ||
| include: | ||
| - python-version: '3.8' | ||
| - python-version: '3.12' | ||
| toxenv: 'docs' | ||
@@ -40,3 +40,3 @@ container: | ||
| apt-get update -q | ||
| apt-get install -y build-essential git libffi-dev python${{ matrix.python-version }} python${{ matrix.python-version }}-dev python${{ matrix.python-version }}-venv python3-distutils python3-pip python3-setuptools python3-yaml | ||
| apt-get install -y build-essential git python${{ matrix.python-version }} python${{ matrix.python-version }}-dev python${{ matrix.python-version }}-venv python3-distutils python3-pip python3-setuptools python3-yaml | ||
| - name: Install tox | ||
@@ -43,0 +43,0 @@ run: | |
@@ -18,11 +18,13 @@ # Run tox tests on Ubuntu Docker images using GIFT PPA | ||
| - python-version: '3.7' | ||
| toxenv: 'py37' | ||
| toxenv: 'py37,wheel' | ||
| - python-version: '3.8' | ||
| toxenv: 'py38' | ||
| toxenv: 'py38,wheel' | ||
| - python-version: '3.9' | ||
| toxenv: 'py39' | ||
| toxenv: 'py39,wheel' | ||
| - python-version: '3.10' | ||
| toxenv: 'py310' | ||
| toxenv: 'py310,wheel' | ||
| - python-version: '3.11' | ||
| toxenv: 'py311' | ||
| toxenv: 'py311,wheel' | ||
| - python-version: '3.12' | ||
| toxenv: 'py312,wheel' | ||
| container: | ||
@@ -48,3 +50,3 @@ image: ubuntu:22.04 | ||
| apt-get update -q | ||
| apt-get install -y build-essential git libffi-dev python${{ matrix.python-version }} python${{ matrix.python-version }}-dev python${{ matrix.python-version }}-venv python3-distutils python3-pip python3-setuptools python3-yaml | ||
| apt-get install -y build-essential git python${{ matrix.python-version }} python${{ matrix.python-version }}-dev python${{ matrix.python-version }}-venv python3-distutils python3-pip python3-setuptools python3-yaml | ||
| - name: Install tox | ||
@@ -63,4 +65,4 @@ run: | | ||
| include: | ||
| - python-version: '3.8' | ||
| toxenv: 'py38,coverage' | ||
| - python-version: '3.10' | ||
| toxenv: 'coverage' | ||
| container: | ||
@@ -86,3 +88,3 @@ image: ubuntu:22.04 | ||
| apt-get update -q | ||
| apt-get install -y build-essential git libffi-dev python${{ matrix.python-version }} python${{ matrix.python-version }}-dev python${{ matrix.python-version }}-venv python3-distutils python3-pip python3-setuptools python3-yaml | ||
| apt-get install -y build-essential git python${{ matrix.python-version }} python${{ matrix.python-version }}-dev python${{ matrix.python-version }}-venv python3-distutils python3-pip python3-setuptools python3-yaml | ||
| - name: Install tox | ||
@@ -103,3 +105,3 @@ run: | | ||
| include: | ||
| - python-version: '3.11' | ||
| - python-version: '3.12' | ||
| toxenv: 'lint' | ||
@@ -126,3 +128,3 @@ container: | ||
| apt-get update -q | ||
| apt-get install -y build-essential git libffi-dev python${{ matrix.python-version }} python${{ matrix.python-version }}-dev python${{ matrix.python-version }}-venv python3-distutils python3-pip python3-setuptools python3-yaml | ||
| apt-get install -y build-essential git python${{ matrix.python-version }} python${{ matrix.python-version }}-dev python${{ matrix.python-version }}-venv python3-distutils python3-pip python3-setuptools python3-yaml | ||
| - name: Install tox | ||
@@ -129,0 +131,0 @@ run: | |
| Metadata-Version: 2.1 | ||
| Name: acstore | ||
| Version: 20230519 | ||
| Version: 20240128 | ||
| Summary: Attribute Container Storage (ACStore). | ||
@@ -9,9 +9,11 @@ Home-page: https://github.com/log2timeline/acstore | ||
| License: Apache License, Version 2.0 | ||
| Classifier: | ||
| Classifier: Environment :: Console | ||
| Classifier: Operating System :: OS Independent | ||
| Classifier: Development Status :: 3 - Alpha | ||
| Classifier: Programming Language :: Python | ||
| Requires-Python: >=3.7 | ||
| Description-Content-Type: text/plain | ||
| License-File: ACKNOWLEDGEMENTS | ||
| License-File: AUTHORS | ||
| License-File: LICENSE | ||
| License-File: README | ||
| ACStore, or Attribute Container Storage, provides a stand-alone implementation to read and write attribute container storage files. |
@@ -1,2 +0,1 @@ | ||
| pip>=7.0.0 | ||
| PyYAML>=3.10 |
| .pylintrc | ||
| .style.yapf | ||
| .readthedocs.yaml | ||
| .yamllint.yaml | ||
@@ -12,2 +12,3 @@ ACKNOWLEDGEMENTS | ||
| dependencies.ini | ||
| pyproject.toml | ||
| requirements.txt | ||
@@ -38,2 +39,3 @@ run_tests.py | ||
| acstore/helpers/__init__.py | ||
| acstore/helpers/json_serializer.py | ||
| acstore/helpers/schema.py | ||
@@ -71,2 +73,3 @@ acstore/helpers/yaml_definitions_file.py | ||
| tests/helpers/__init__.py | ||
| tests/helpers/json_serializer.py | ||
| tests/helpers/schema.py | ||
@@ -73,0 +76,0 @@ tests/helpers/yaml_definitions_file.py |
+1
-0
| [project] | ||
| name: acstore | ||
| name_description: ACStore | ||
| status: alpha | ||
| maintainer: Log2Timeline maintainers <log2timeline-maintainers@googlegroups.com> | ||
@@ -5,0 +6,0 @@ homepage_url: https://github.com/log2timeline/acstore |
@@ -8,2 +8,2 @@ # -*- coding: utf-8 -*- | ||
| __version__ = '20230519' | ||
| __version__ = '20240128' |
@@ -52,8 +52,8 @@ # -*- coding: utf-8 -*- | ||
| This is the base class for those object that exists primarily as | ||
| a container of attributes with basic accessors and mutators. | ||
| This is the base class for those object that exists primarily as a container | ||
| of attributes with basic accessors and mutators. | ||
| The CONTAINER_TYPE class attribute contains a string that identifies | ||
| the container type, for example the container type "event" identifiers | ||
| an event object. | ||
| The CONTAINER_TYPE class attribute contains a string that identifies the | ||
| container type, for example the container type "event" identifiers an event | ||
| object. | ||
@@ -60,0 +60,0 @@ Attributes are public class members of a serializable type. Protected and |
@@ -78,3 +78,3 @@ # -*- coding: utf-8 -*- | ||
| if not container_class: | ||
| raise ValueError(f'Unsupported container type: {container_type:s}') | ||
| raise ValueError(f'Unsupported container type: {container_type!s}') | ||
@@ -81,0 +81,0 @@ return getattr(container_class, 'SCHEMA', {}) |
@@ -25,5 +25,5 @@ # -*- coding: utf-8 -*- | ||
| Raises: | ||
| OSError: if the store cannot be read from. | ||
| IOError: if the store cannot be read from. | ||
| Raises: | ||
| OSError: if the store cannot be read from. | ||
| IOError: if the store cannot be read from. | ||
| """ | ||
@@ -30,0 +30,0 @@ if not self._is_open: |
@@ -26,3 +26,3 @@ # -*- coding: utf-8 -*- | ||
| name: 'windows_eventlog_message_file' | ||
| name: windows_eventlog_message_file | ||
| attributes: | ||
@@ -47,4 +47,4 @@ - name: path | ||
| _SUPPORTED_KEYS = frozenset([ | ||
| 'name', | ||
| 'attributes']) | ||
| 'attributes', | ||
| 'name']) | ||
@@ -51,0 +51,0 @@ def _ReadDefinition(self, definition_values): |
+58
-24
@@ -85,5 +85,5 @@ # -*- coding: utf-8 -*- | ||
| Raises: | ||
| OSError: if the store cannot be read from. | ||
| IOError: if the store cannot be read from. | ||
| Raises: | ||
| OSError: if the store cannot be read from. | ||
| IOError: if the store cannot be read from. | ||
| """ | ||
@@ -95,5 +95,5 @@ | ||
| Raises: | ||
| OSError: if the store cannot be written to. | ||
| IOError: if the store cannot be written to. | ||
| Raises: | ||
| OSError: if the store cannot be written to. | ||
| IOError: if the store cannot be written to. | ||
| """ | ||
@@ -113,4 +113,4 @@ | ||
| @abc.abstractmethod | ||
| def _WriteNewAttributeContainer(self, container): | ||
| """Writes a new attribute container to the store. | ||
| def _WriteExistingAttributeContainer(self, container): | ||
| """Writes an existing attribute container to the store. | ||
@@ -122,4 +122,4 @@ Args: | ||
| @abc.abstractmethod | ||
| def _WriteExistingAttributeContainer(self, container): | ||
| """Writes an existing attribute container to the store. | ||
| def _WriteNewAttributeContainer(self, container): | ||
| """Writes a new attribute container to the store. | ||
@@ -157,8 +157,2 @@ Args: | ||
| AttributeContainer: attribute container or None if not available. | ||
| Raises: | ||
| IOError: when the store is closed or if an unsupported identifier is | ||
| provided. | ||
| OSError: when the store is closed or if an unsupported identifier is | ||
| provided. | ||
| """ | ||
@@ -176,6 +170,2 @@ | ||
| AttributeContainer: attribute container or None if not available. | ||
| Raises: | ||
| IOError: when the store is closed. | ||
| OSError: when the store is closed. | ||
| """ | ||
@@ -194,6 +184,2 @@ | ||
| generator(AttributeContainer): attribute container generator. | ||
| Raises: | ||
| IOError: when the store is closed. | ||
| OSError: when the store is closed. | ||
| """ | ||
@@ -248,1 +234,49 @@ | ||
| self._WriteExistingAttributeContainer(container) | ||
| class AttributeContainerStoreWithReadCache(AttributeContainerStore): | ||
| """Interface of an attribute container store with read cache. | ||
| Attributes: | ||
| format_version (int): storage format version. | ||
| """ | ||
| # pylint: disable=abstract-method | ||
| # The maximum number of cached attribute containers | ||
| _MAXIMUM_CACHED_CONTAINERS = 32 * 1024 | ||
| def __init__(self): | ||
| """Initializes an attribute container store with read cache.""" | ||
| super(AttributeContainerStoreWithReadCache, self).__init__() | ||
| self._attribute_container_cache = collections.OrderedDict() | ||
| def _CacheAttributeContainerByIndex(self, attribute_container, index): | ||
| """Caches a specific attribute container. | ||
| Args: | ||
| attribute_container (AttributeContainer): attribute container. | ||
| index (int): attribute container index. | ||
| """ | ||
| if len(self._attribute_container_cache) >= self._MAXIMUM_CACHED_CONTAINERS: | ||
| self._attribute_container_cache.popitem(last=True) | ||
| lookup_key = f'{attribute_container.CONTAINER_TYPE:s}.{index:d}' | ||
| self._attribute_container_cache[lookup_key] = attribute_container | ||
| self._attribute_container_cache.move_to_end(lookup_key, last=False) | ||
| def _GetCachedAttributeContainer(self, container_type, index): | ||
| """Retrieves a specific cached attribute container. | ||
| Args: | ||
| container_type (str): attribute container type. | ||
| index (int): attribute container index. | ||
| Returns: | ||
| AttributeContainer: attribute container or None if not available. | ||
| """ | ||
| lookup_key = f'{container_type:s}.{index:d}' | ||
| attribute_container = self._attribute_container_cache.get(lookup_key, None) | ||
| if attribute_container: | ||
| self._attribute_container_cache.move_to_end(lookup_key, last=False) | ||
| return attribute_container |
+41
-69
@@ -5,4 +5,4 @@ # -*- coding: utf-8 -*- | ||
| import ast | ||
| import collections | ||
| import itertools | ||
| import json | ||
| import os | ||
@@ -126,3 +126,4 @@ import pathlib | ||
| data_type, 'json') | ||
| value = serializer.DeserializeValue(value) | ||
| json_dict = json.loads(value) | ||
| value = serializer.DeserializeValue(json_dict) | ||
@@ -165,3 +166,4 @@ return value | ||
| return serializer.SerializeValue(value) | ||
| json_dict = serializer.SerializeValue(value) | ||
| return json.dumps(json_dict) | ||
@@ -171,3 +173,4 @@ return value | ||
| class SQLiteAttributeContainerStore(interface.AttributeContainerStore): | ||
| class SQLiteAttributeContainerStore( | ||
| interface.AttributeContainerStoreWithReadCache): | ||
| """SQLite-based attribute container store. | ||
@@ -212,5 +215,2 @@ | ||
| # The maximum number of cached attribute containers | ||
| _MAXIMUM_CACHED_CONTAINERS = 32 * 1024 | ||
| _MAXIMUM_WRITE_CACHE_SIZE = 50 | ||
@@ -221,3 +221,2 @@ | ||
| super(SQLiteAttributeContainerStore, self).__init__() | ||
| self._attribute_container_cache = collections.OrderedDict() | ||
| self._connection = None | ||
@@ -233,16 +232,2 @@ self._cursor = None | ||
| def _CacheAttributeContainerByIndex(self, attribute_container, index): | ||
| """Caches a specific attribute container. | ||
| Args: | ||
| attribute_container (AttributeContainer): attribute container. | ||
| index (int): attribute container index. | ||
| """ | ||
| if len(self._attribute_container_cache) >= self._MAXIMUM_CACHED_CONTAINERS: | ||
| self._attribute_container_cache.popitem(last=True) | ||
| lookup_key = f'{attribute_container.CONTAINER_TYPE:s}.{index:d}' | ||
| self._attribute_container_cache[lookup_key] = attribute_container | ||
| self._attribute_container_cache.move_to_end(lookup_key, last=False) | ||
| def _CacheAttributeContainerForWrite( | ||
@@ -525,11 +510,10 @@ self, container_type, column_names, values): | ||
| def _GetCachedAttributeContainer(self, container_type, index): | ||
| """Retrieves a specific cached attribute container. | ||
| def _GetNumberOfAttributeContainerRows(self, container_type): | ||
| """Retrieves the number of attribute container rows. | ||
| Args: | ||
| container_type (str): attribute container type. | ||
| index (int): attribute container index. | ||
| Returns: | ||
| AttributeContainer: attribute container or None if not available. | ||
| int: the number of rows of a specified attribute container type. | ||
@@ -540,8 +524,25 @@ Raises: | ||
| """ | ||
| lookup_key = f'{container_type:s}.{index:d}' | ||
| attribute_container = self._attribute_container_cache.get(lookup_key, None) | ||
| if attribute_container: | ||
| self._attribute_container_cache.move_to_end(lookup_key, last=False) | ||
| return attribute_container | ||
| self._CommitWriteCache(container_type) | ||
| if not self._HasTable(container_type): | ||
| return 0 | ||
| # Note that this is SQLite specific, and will give inaccurate results if | ||
| # there are DELETE commands run on the table. acstore does not run any | ||
| # DELETE commands. | ||
| query = f'SELECT MAX(_ROWID_) FROM {container_type:s} LIMIT 1' | ||
| try: | ||
| self._cursor.execute(query) | ||
| except (sqlite3.InterfaceError, sqlite3.OperationalError) as exception: | ||
| raise IOError(( | ||
| f'Unable to query attribute container store with error: ' | ||
| f'{exception!s}')) | ||
| row = self._cursor.fetchone() | ||
| if not row: | ||
| return 0 | ||
| return row[0] or 0 | ||
| def _HasTable(self, table_name): | ||
@@ -574,5 +575,5 @@ """Determines if a specific table exists. | ||
| Raises: | ||
| IOError: when the attribute container store is closed. | ||
| OSError: when the attribute container store is closed. | ||
| Raises: | ||
| IOError: when the attribute container store is closed. | ||
| OSError: when the attribute container store is closed. | ||
| """ | ||
@@ -848,3 +849,3 @@ if not self._is_open: | ||
| if not self._is_open: | ||
| raise IOError('Storage file already closed.') | ||
| raise IOError('Attribute container store already closed.') | ||
@@ -987,30 +988,5 @@ if self._connection: | ||
| int: the number of containers of a specified type. | ||
| Raises: | ||
| IOError: when there is an error querying the attribute container store. | ||
| OSError: when there is an error querying the attribute container store. | ||
| """ | ||
| self._CommitWriteCache(container_type) | ||
| return self._attribute_container_sequence_numbers[container_type] | ||
| if not self._HasTable(container_type): | ||
| return 0 | ||
| # Note that this is SQLite specific, and will give inaccurate results if | ||
| # there are DELETE commands run on the table. acstore does not run any | ||
| # DELETE commands. | ||
| query = f'SELECT MAX(_ROWID_) FROM {container_type:s} LIMIT 1' | ||
| try: | ||
| self._cursor.execute(query) | ||
| except (sqlite3.InterfaceError, sqlite3.OperationalError) as exception: | ||
| raise IOError(( | ||
| f'Unable to query attribute container store with error: ' | ||
| f'{exception!s}')) | ||
| row = self._cursor.fetchone() | ||
| if not row: | ||
| return 0 | ||
| return row[0] or 0 | ||
| def HasAttributeContainers(self, container_type): | ||
@@ -1025,9 +1001,4 @@ """Determines if store contains a specific type of attribute containers. | ||
| containers. | ||
| Raises: | ||
| IOError: when there is an error querying the attribute container store. | ||
| OSError: when there is an error querying the attribute container store. | ||
| """ | ||
| count = self.GetNumberOfAttributeContainers(container_type) | ||
| return count > 0 | ||
| return self._attribute_container_sequence_numbers[container_type] > 0 | ||
@@ -1050,3 +1021,3 @@ def Open(self, path=None, read_only=True, **unused_kwargs): # pylint: disable=arguments-differ | ||
| if self._is_open: | ||
| raise IOError('Storage file already opened.') | ||
| raise IOError('Attribute container store already opened.') | ||
@@ -1114,4 +1085,5 @@ if not path: | ||
| for container_type in self._containers_manager.GetContainerTypes(): | ||
| next_sequence_number = self.GetNumberOfAttributeContainers(container_type) | ||
| next_sequence_number = self._GetNumberOfAttributeContainerRows( | ||
| container_type) | ||
| self._SetAttributeContainerNextSequenceNumber( | ||
| container_type, next_sequence_number) |
+32
-15
| environment: | ||
| matrix: | ||
| - DESCRIPTION: "Windows with 32-bit Python 3.11" | ||
| - DESCRIPTION: "Run tests on Windows with 32-bit Python 3.12" | ||
| MACHINE_TYPE: "x86" | ||
| APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 | ||
| PYTHON: "C:\\Python311" | ||
| PYTHON_VERSION: "3.11" | ||
| PYTHON: "C:\\Python312" | ||
| PYTHON_VERSION: "3.12" | ||
| L2TBINARIES_TRACK: "dev" | ||
| - DESCRIPTION: "Windows with 64-bit Python 3.11" | ||
| TARGET: tests | ||
| - DESCRIPTION: "Run tests on Windows with 64-bit Python 3.12" | ||
| MACHINE_TYPE: "amd64" | ||
| APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 | ||
| PYTHON: "C:\\Python311-x64" | ||
| PYTHON_VERSION: "3.11" | ||
| PYTHON: "C:\\Python312-x64" | ||
| PYTHON_VERSION: "3.12" | ||
| L2TBINARIES_TRACK: "dev" | ||
| - DESCRIPTION: "Mac OS with Python 3.11" | ||
| TARGET: tests | ||
| - DESCRIPTION: "Build wheel on Windows with 32-bit Python 3.12" | ||
| MACHINE_TYPE: "amd64" | ||
| APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 | ||
| PYTHON: "C:\\Python312-x64" | ||
| PYTHON_VERSION: "3.12" | ||
| L2TBINARIES_TRACK: "dev" | ||
| TARGET: wheel | ||
| - DESCRIPTION: "Build wheel on Windows with 64-bit Python 3.12" | ||
| MACHINE_TYPE: "amd64" | ||
| APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 | ||
| PYTHON: "C:\\Python312-x64" | ||
| PYTHON_VERSION: "3.12" | ||
| L2TBINARIES_TRACK: "dev" | ||
| TARGET: wheel | ||
| - DESCRIPTION: "Run tests on Mac OS with Python 3.12" | ||
| APPVEYOR_BUILD_WORKER_IMAGE: macos-monterey | ||
| HOMEBREW_NO_INSTALL_CLEANUP: 1 | ||
| TARGET: tests | ||
| install: | ||
| - cmd: "%PYTHON%\\python.exe -m pip install -U pip setuptools twine wheel" | ||
| - cmd: "%PYTHON%\\python.exe -m pip install pywin32 WMI" | ||
| - cmd: "%PYTHON%\\python.exe %PYTHON%\\Scripts\\pywin32_postinstall.py -install" | ||
| - cmd: "%PYTHON%\\python.exe -m pip install -U build pip setuptools twine wheel" | ||
| - ps: If ($isWindows) { .\config\appveyor\install.ps1 } | ||
@@ -27,9 +42,11 @@ - sh: config/appveyor/install.sh | ||
| build_script: | ||
| - cmd: "%PYTHON%\\python.exe setup.py bdist_wheel" | ||
| - cmd: IF [%TARGET%]==[wheel] ( | ||
| "%PYTHON%\\python.exe" -m build --wheel ) | ||
| test_script: | ||
| - cmd: "%PYTHON%\\python.exe run_tests.py" | ||
| - cmd: IF EXIST "tests\\end-to-end.py" ( | ||
| set PYTHONPATH=. && | ||
| "%PYTHON%\\python.exe" "tests\\end-to-end.py" --debug -c "config\\end-to-end.ini" ) | ||
| - cmd: IF [%TARGET%]==[tests] ( | ||
| "%PYTHON%\\python.exe" run_tests.py && | ||
| IF EXIST "tests\\end-to-end.py" ( | ||
| set PYTHONPATH=. && | ||
| "%PYTHON%\\python.exe" "tests\\end-to-end.py" --debug -c "config\\end-to-end.ini" ) ) | ||
| - sh: config/appveyor/runtests.sh | ||
@@ -36,0 +53,0 @@ |
@@ -24,5 +24,5 @@ # Script to set up tests on AppVeyor Windows. | ||
| $Output = Invoke-Expression -Command "& '${env:PYTHON}\python.exe' ..\l2tdevtools\tools\update.py --download-directory dependencies --machine-type ${env:MACHINE_TYPE} --msi-targetdir ${env:PYTHON} --track ${env:L2TBINARIES_TRACK} ${Dependencies} 2>&1" | %{ "$_" } | ||
| $Output = Invoke-Expression -Command "& '${env:PYTHON}\python.exe' ..\l2tdevtools\tools\update.py --download-directory dependencies --machine-type ${env:MACHINE_TYPE} --track ${env:L2TBINARIES_TRACK} ${Dependencies} 2>&1" | %{ "$_" } | ||
| Write-Host (${Output} | Out-String) | ||
| } | ||
@@ -6,3 +6,3 @@ # Script to set up tests on AppVeyor MacOS. | ||
| brew update -q | ||
| brew install -q gettext gnu-sed python@3.11 tox || true | ||
| brew install -q gettext gnu-sed python@3.12 tox || true | ||
@@ -12,5 +12,5 @@ #!/bin/sh | ||
| # Set the following environment variables to ensure tox can find Python 3.11. | ||
| export PATH="/usr/local/opt/python@3.11/bin:${PATH}"; | ||
| # Set the following environment variables to ensure tox can find Python 3.12. | ||
| export PATH="/usr/local/opt/python@3.12/bin:${PATH}"; | ||
| tox -e py311 | ||
| tox -e py312 |
+2
-1
@@ -55,2 +55,3 @@ # -*- coding: utf-8 -*- | ||
| napoleon_numpy_docstring = False | ||
| napoleon_include_init_with_doc = True | ||
| napoleon_include_private_with_doc = False | ||
@@ -107,3 +108,3 @@ napoleon_include_special_with_doc = True | ||
| app (sphinx.application.Sphinx): Sphinx application. Required by the | ||
| Sphinx event callback API. | ||
| the Sphinx event callback API. | ||
| """ | ||
@@ -110,0 +111,0 @@ current_directory = os.path.abspath(os.path.dirname(__file__)) |
@@ -0,6 +1,7 @@ | ||
| certifi >= 2023.11.17 | ||
| docutils | ||
| Markdown | ||
| recommonmark | ||
| sphinx >= 4.1.0, < 5.2.0 | ||
| sphinx >= 4.1.0 | ||
| sphinx-markdown-tables | ||
| sphinx-rtd-theme >= 0.5.1 |
@@ -7,2 +7,10 @@ acstore.helpers package | ||
| acstore.helpers.json\_serializer module | ||
| --------------------------------------- | ||
| .. automodule:: acstore.helpers.json_serializer | ||
| :members: | ||
| :undoc-members: | ||
| :show-inheritance: | ||
| acstore.helpers.schema module | ||
@@ -9,0 +17,0 @@ ----------------------------- |
+6
-4
| Metadata-Version: 2.1 | ||
| Name: acstore | ||
| Version: 20230519 | ||
| Version: 20240128 | ||
| Summary: Attribute Container Storage (ACStore). | ||
@@ -9,9 +9,11 @@ Home-page: https://github.com/log2timeline/acstore | ||
| License: Apache License, Version 2.0 | ||
| Classifier: | ||
| Classifier: Environment :: Console | ||
| Classifier: Operating System :: OS Independent | ||
| Classifier: Development Status :: 3 - Alpha | ||
| Classifier: Programming Language :: Python | ||
| Requires-Python: >=3.7 | ||
| Description-Content-Type: text/plain | ||
| License-File: ACKNOWLEDGEMENTS | ||
| License-File: AUTHORS | ||
| License-File: LICENSE | ||
| License-File: README | ||
| ACStore, or Attribute Container Storage, provides a stand-alone implementation to read and write attribute container storage files. |
+0
-1
@@ -1,2 +0,1 @@ | ||
| pip >= 7.0.0 | ||
| PyYAML >= 3.10 |
+36
-3
| [metadata] | ||
| license_files = LICENSE | ||
| name = acstore | ||
| version = 20240128 | ||
| description = Attribute Container Storage (ACStore). | ||
| long_description = ACStore, or Attribute Container Storage, provides a stand-alone implementation to read and write attribute container storage files. | ||
| long_description_content_type = text/plain | ||
| url = https://github.com/log2timeline/acstore | ||
| maintainer = Log2Timeline maintainers | ||
| maintainer_email = log2timeline-maintainers@googlegroups.com | ||
| license = Apache License, Version 2.0 | ||
| license_files = | ||
| ACKNOWLEDGEMENTS | ||
| AUTHORS | ||
| LICENSE | ||
| README | ||
| classifiers = | ||
| Development Status :: 3 - Alpha | ||
| Programming Language :: Python | ||
| [options] | ||
| install_requires = file:requirements.txt | ||
| package_dir = | ||
| acstore = acstore | ||
| packages = find: | ||
| python_requires = >=3.7 | ||
| [options.packages.find] | ||
| exclude = | ||
| docs | ||
| tests | ||
| tests.* | ||
| utils | ||
| where = . | ||
| [bdist_rpm] | ||
| release = 1 | ||
| packager = Log2Timeline maintainers <log2timeline-maintainers@googlegroups.com> | ||
| doc_files = ACKNOWLEDGEMENTS | ||
| doc_files = | ||
| ACKNOWLEDGEMENTS | ||
| AUTHORS | ||
@@ -12,3 +44,4 @@ LICENSE | ||
| build_requires = python3-setuptools | ||
| requires = python3-pyyaml >= 3.10 | ||
| requires = | ||
| python3-pyyaml >= 3.10 | ||
@@ -15,0 +48,0 @@ [bdist_wheel] |
+2
-214
@@ -5,217 +5,5 @@ #!/usr/bin/env python | ||
| import os | ||
| import pkg_resources | ||
| import sys | ||
| from setuptools import setup | ||
| try: | ||
| from setuptools import find_packages, setup | ||
| except ImportError: | ||
| from distutils.core import find_packages, setup | ||
| try: | ||
| from distutils.command.bdist_msi import bdist_msi | ||
| except ImportError: | ||
| bdist_msi = None | ||
| try: | ||
| from distutils.command.bdist_rpm import bdist_rpm | ||
| except ImportError: | ||
| bdist_rpm = None | ||
| version_tuple = (sys.version_info[0], sys.version_info[1]) | ||
| if version_tuple < (3, 7): | ||
| print(f'Unsupported Python version: {sys.version:s}, version 3.7 or higher ' | ||
| f'required.') | ||
| sys.exit(1) | ||
| # Change PYTHONPATH to include acstore so that we can get the version. | ||
| sys.path.insert(0, '.') | ||
| import acstore # pylint: disable=wrong-import-position | ||
| if not bdist_msi: | ||
| BdistMSICommand = None | ||
| else: | ||
| class BdistMSICommand(bdist_msi): | ||
| """Custom handler for the bdist_msi command.""" | ||
| # pylint: disable=invalid-name | ||
| def run(self): | ||
| """Builds an MSI.""" | ||
| # Command bdist_msi does not support the library version, neither a date | ||
| # as a version but if we suffix it with .1 everything is fine. | ||
| self.distribution.metadata.version += '.1' | ||
| bdist_msi.run(self) | ||
| if not bdist_rpm: | ||
| BdistRPMCommand = None | ||
| else: | ||
| class BdistRPMCommand(bdist_rpm): | ||
| """Custom handler for the bdist_rpm command.""" | ||
| # pylint: disable=invalid-name | ||
| def _make_spec_file(self): | ||
| """Generates the text of an RPM spec file. | ||
| Returns: | ||
| list[str]: lines of the RPM spec file. | ||
| """ | ||
| # Note that bdist_rpm can be an old style class. | ||
| if issubclass(BdistRPMCommand, object): | ||
| spec_file = super(BdistRPMCommand, self)._make_spec_file() | ||
| else: | ||
| spec_file = bdist_rpm._make_spec_file(self) | ||
| python_package = 'python3' | ||
| description = [] | ||
| requires = '' | ||
| summary = '' | ||
| in_description = False | ||
| python_spec_file = [] | ||
| for line in iter(spec_file): | ||
| if line.startswith('Summary: '): | ||
| summary = line[9:] | ||
| elif line.startswith('BuildRequires: '): | ||
| line = (f'BuildRequires: {python_package:s}-setuptools, ' | ||
| f'{python_package:s}-devel') | ||
| elif line.startswith('Requires: '): | ||
| requires = line[10:] | ||
| continue | ||
| elif line.startswith('%description'): | ||
| in_description = True | ||
| elif line.startswith('python setup.py build'): | ||
| if python_package == 'python3': | ||
| line = '%py3_build' | ||
| else: | ||
| line = '%py2_build' | ||
| elif line.startswith('python setup.py install'): | ||
| if python_package == 'python3': | ||
| line = '%py3_install' | ||
| else: | ||
| line = '%py2_install' | ||
| elif line.startswith('%files'): | ||
| lines = [ | ||
| f'%files -n {python_package:s}-%{{name}}', | ||
| '%defattr(644,root,root,755)', | ||
| '%license LICENSE', | ||
| '%doc ACKNOWLEDGEMENTS AUTHORS README'] | ||
| lines.extend([ | ||
| '%{python3_sitelib}/acstore/*.py', | ||
| '%{python3_sitelib}/acstore/*/*.py', | ||
| '%{python3_sitelib}/acstore*.egg-info/*', | ||
| '', | ||
| '%exclude %{_prefix}/share/doc/*', | ||
| '%exclude %{python3_sitelib}/acstore/__pycache__/*', | ||
| '%exclude %{python3_sitelib}/acstore/*/__pycache__/*']) | ||
| python_spec_file.extend(lines) | ||
| break | ||
| elif line.startswith('%prep'): | ||
| in_description = False | ||
| python_spec_file.append(f'%package -n {python_package:s}-%{{name}}') | ||
| python_summary = f'Python 3 module of {summary:s}' | ||
| if requires: | ||
| python_spec_file.append(f'Requires: {requires:s}') | ||
| python_spec_file.extend([ | ||
| f'Summary: {python_summary:s}', | ||
| '', | ||
| f'%description -n {python_package:s}-%{{name}}']) | ||
| python_spec_file.extend(description) | ||
| elif in_description: | ||
| # Ignore leading white lines in the description. | ||
| if not description and not line: | ||
| continue | ||
| description.append(line) | ||
| python_spec_file.append(line) | ||
| return python_spec_file | ||
| def parse_requirements_from_file(path): | ||
| """Parses requirements from a requirements file. | ||
| Args: | ||
| path (str): path to the requirements file. | ||
| Returns: | ||
| list[str]: name and optional version information of the required packages. | ||
| """ | ||
| requirements = [] | ||
| if os.path.isfile(path): | ||
| with open(path, 'r') as file_object: | ||
| file_contents = file_object.read() | ||
| for requirement in pkg_resources.parse_requirements(file_contents): | ||
| try: | ||
| name = str(requirement.req) | ||
| except AttributeError: | ||
| name = str(requirement) | ||
| if not name.startswith('pip '): | ||
| requirements.append(name) | ||
| return requirements | ||
| acstore_description = ( | ||
| 'Attribute Container Storage (ACStore).') | ||
| acstore_long_description = ( | ||
| 'ACStore, or Attribute Container Storage, provides a stand-alone ' | ||
| 'implementation to read and write attribute container storage files.') | ||
| command_classes = {} | ||
| if BdistMSICommand: | ||
| command_classes['bdist_msi'] = BdistMSICommand | ||
| if BdistRPMCommand: | ||
| command_classes['bdist_rpm'] = BdistRPMCommand | ||
| setup( | ||
| name='acstore', | ||
| version=acstore.__version__, | ||
| description=acstore_description, | ||
| long_description=acstore_long_description, | ||
| long_description_content_type='text/plain', | ||
| license='Apache License, Version 2.0', | ||
| url='https://github.com/log2timeline/acstore', | ||
| maintainer='Log2Timeline maintainers', | ||
| maintainer_email='log2timeline-maintainers@googlegroups.com', | ||
| cmdclass=command_classes, | ||
| classifiers=[ | ||
| '', | ||
| 'Environment :: Console', | ||
| 'Operating System :: OS Independent', | ||
| 'Programming Language :: Python', | ||
| ], | ||
| packages=find_packages('.', exclude=[ | ||
| 'docs', 'tests', 'tests.*', 'utils']), | ||
| package_dir={ | ||
| 'acstore': 'acstore' | ||
| }, | ||
| data_files=[ | ||
| ('share/doc/acstore', [ | ||
| 'ACKNOWLEDGEMENTS', 'AUTHORS', 'LICENSE', 'README']), | ||
| ], | ||
| install_requires=parse_requirements_from_file('requirements.txt'), | ||
| tests_require=parse_requirements_from_file('test_requirements.txt'), | ||
| ) | ||
| setup() |
| # YAML-based attribute container definitions file. | ||
| --- | ||
| name: 'windows_eventlog_message_file' | ||
| name: windows_eventlog_message_file | ||
| attributes: | ||
| - name: 'path' | ||
| type: 'str' | ||
| - name: 'windows_path' | ||
| type: 'str' | ||
| - name: path | ||
| type: str | ||
| - name: windows_path | ||
| type: str |
@@ -35,8 +35,8 @@ #!/usr/bin/env python3 | ||
| attribute_container = interface.AttributeContainer() | ||
| attribute_container.attribute_name = 'attribute_name' | ||
| attribute_container.attribute_value = 'attribute_value' | ||
| attribute_container.attribute_name = 'MyName' | ||
| attribute_container.attribute_value = 'MyValue' | ||
| expected_dict = { | ||
| 'attribute_name': 'attribute_name', | ||
| 'attribute_value': 'attribute_value'} | ||
| 'attribute_name': 'MyName', | ||
| 'attribute_value': 'MyValue'} | ||
@@ -51,4 +51,4 @@ test_dict = attribute_container.CopyToDict() | ||
| attribute_container._protected_attribute = 'protected' | ||
| attribute_container.attribute_name = 'attribute_name' | ||
| attribute_container.attribute_value = 'attribute_value' | ||
| attribute_container.attribute_name = 'MyName' | ||
| attribute_container.attribute_value = 'MyValue' | ||
@@ -75,8 +75,8 @@ expected_attribute_names = ['attribute_name', 'attribute_value'] | ||
| attribute_container._protected_attribute = 'protected' | ||
| attribute_container.attribute_name = 'attribute_name' | ||
| attribute_container.attribute_value = 'attribute_value' | ||
| attribute_container.attribute_name = 'MyName' | ||
| attribute_container.attribute_value = 'MyValue' | ||
| expected_attributes = [ | ||
| ('attribute_name', 'attribute_name'), | ||
| ('attribute_value', 'attribute_value')] | ||
| ('attribute_name', 'MyName'), | ||
| ('attribute_value', 'MyValue')] | ||
@@ -92,4 +92,4 @@ attributes = sorted(attribute_container.GetAttributes()) | ||
| ('_protected_attribute', 'protected'), | ||
| ('attribute_name', 'attribute_name'), | ||
| ('attribute_value', 'attribute_value')] | ||
| ('attribute_name', 'MyName'), | ||
| ('attribute_value', 'MyValue')] | ||
@@ -104,4 +104,4 @@ attributes = sorted(attribute_container.GetAttributes()) | ||
| attribute_container._protected_attribute = 'protected' | ||
| attribute_container.attribute_name = 'attribute_name' | ||
| attribute_container.attribute_value = 'attribute_value' | ||
| attribute_container.attribute_name = 'MyName' | ||
| attribute_container.attribute_value = 'MyValue' | ||
@@ -116,3 +116,3 @@ attribute_values_hash1 = attribute_container.GetAttributeValuesHash() | ||
| attribute_container.attribute_value = 'attribute_value' | ||
| attribute_container.attribute_value = 'MyValue' | ||
@@ -130,4 +130,4 @@ setattr(attribute_container, '_SERIALIZABLE_PROTECTED_ATTRIBUTES', [ | ||
| attribute_container._protected_attribute = 'protected' | ||
| attribute_container.attribute_name = 'attribute_name' | ||
| attribute_container.attribute_value = 'attribute_value' | ||
| attribute_container.attribute_name = 'MyName' | ||
| attribute_container.attribute_value = 'MyValue' | ||
@@ -142,3 +142,3 @@ attribute_values_string1 = attribute_container.GetAttributeValuesString() | ||
| attribute_container.attribute_value = 'attribute_value' | ||
| attribute_container.attribute_value = 'MyValue' | ||
@@ -145,0 +145,0 @@ setattr(attribute_container, '_SERIALIZABLE_PROTECTED_ATTRIBUTES', [ |
@@ -17,18 +17,19 @@ #!/usr/bin/env python3 | ||
| _TEST_MANAGER = manager.AttributeContainersManager | ||
| def testCreateAttributeContainer(self): | ||
| """Tests the CreateAttributeContainer function.""" | ||
| manager.AttributeContainersManager.RegisterAttributeContainer( | ||
| self._TEST_MANAGER.RegisterAttributeContainer( | ||
| shared_test_lib.TestAttributeContainer) | ||
| try: | ||
| attribute_container = ( | ||
| manager.AttributeContainersManager.CreateAttributeContainer( | ||
| 'test_container')) | ||
| attribute_container = self._TEST_MANAGER.CreateAttributeContainer( | ||
| 'test_container') | ||
| self.assertIsNotNone(attribute_container) | ||
| with self.assertRaises(ValueError): | ||
| manager.AttributeContainersManager.CreateAttributeContainer('bogus') | ||
| self._TEST_MANAGER.CreateAttributeContainer('bogus') | ||
| finally: | ||
| manager.AttributeContainersManager.DeregisterAttributeContainer( | ||
| self._TEST_MANAGER.DeregisterAttributeContainer( | ||
| shared_test_lib.TestAttributeContainer) | ||
@@ -38,11 +39,11 @@ | ||
| """Tests the GetContainerTypes function.""" | ||
| manager.AttributeContainersManager.RegisterAttributeContainer( | ||
| self._TEST_MANAGER.RegisterAttributeContainer( | ||
| shared_test_lib.TestAttributeContainer) | ||
| try: | ||
| container_types = manager.AttributeContainersManager.GetContainerTypes() | ||
| container_types = self._TEST_MANAGER.GetContainerTypes() | ||
| self.assertIn('test_container', container_types) | ||
| finally: | ||
| manager.AttributeContainersManager.DeregisterAttributeContainer( | ||
| self._TEST_MANAGER.DeregisterAttributeContainer( | ||
| shared_test_lib.TestAttributeContainer) | ||
@@ -52,7 +53,7 @@ | ||
| """Tests the GetSchema function.""" | ||
| manager.AttributeContainersManager.RegisterAttributeContainer( | ||
| self._TEST_MANAGER.RegisterAttributeContainer( | ||
| shared_test_lib.TestAttributeContainer) | ||
| try: | ||
| schema = manager.AttributeContainersManager.GetSchema('test_container') | ||
| schema = self._TEST_MANAGER.GetSchema('test_container') | ||
| self.assertIsNotNone(schema) | ||
@@ -62,6 +63,6 @@ self.assertEqual(schema, shared_test_lib.TestAttributeContainer.SCHEMA) | ||
| with self.assertRaises(ValueError): | ||
| manager.AttributeContainersManager.GetSchema('bogus') | ||
| self._TEST_MANAGER.GetSchema('bogus') | ||
| finally: | ||
| manager.AttributeContainersManager.DeregisterAttributeContainer( | ||
| self._TEST_MANAGER.DeregisterAttributeContainer( | ||
| shared_test_lib.TestAttributeContainer) | ||
@@ -71,6 +72,5 @@ | ||
| """Tests the Register and DeregisterAttributeContainer functions.""" | ||
| number_of_classes = len( | ||
| manager.AttributeContainersManager._attribute_container_classes) | ||
| number_of_classes = len(self._TEST_MANAGER._attribute_container_classes) | ||
| manager.AttributeContainersManager.RegisterAttributeContainer( | ||
| self._TEST_MANAGER.RegisterAttributeContainer( | ||
| shared_test_lib.TestAttributeContainer) | ||
@@ -80,15 +80,15 @@ | ||
| self.assertEqual( | ||
| len(manager.AttributeContainersManager._attribute_container_classes), | ||
| len(self._TEST_MANAGER._attribute_container_classes), | ||
| number_of_classes + 1) | ||
| with self.assertRaises(KeyError): | ||
| manager.AttributeContainersManager.RegisterAttributeContainer( | ||
| self._TEST_MANAGER.RegisterAttributeContainer( | ||
| shared_test_lib.TestAttributeContainer) | ||
| finally: | ||
| manager.AttributeContainersManager.DeregisterAttributeContainer( | ||
| self._TEST_MANAGER.DeregisterAttributeContainer( | ||
| shared_test_lib.TestAttributeContainer) | ||
| self.assertEqual( | ||
| len(manager.AttributeContainersManager._attribute_container_classes), | ||
| len(self._TEST_MANAGER._attribute_container_classes), | ||
| number_of_classes) | ||
@@ -95,0 +95,0 @@ |
+35
-0
@@ -62,3 +62,38 @@ #!/usr/bin/env python3 | ||
| class AttributeContainerStoreWithReadCacheTest(test_lib.BaseTestCase): | ||
| """Tests for the attribute container store with read cache.""" | ||
| # pylint: disable=protected-access | ||
| def testCacheAttributeContainerByIndex(self): | ||
| """Tests the _CacheAttributeContainerByIndex function.""" | ||
| attribute_container = test_lib.TestAttributeContainer() | ||
| with test_lib.TempDirectory(): | ||
| test_store = interface.AttributeContainerStoreWithReadCache() | ||
| self.assertEqual(len(test_store._attribute_container_cache), 0) | ||
| test_store._CacheAttributeContainerByIndex(attribute_container, 0) | ||
| self.assertEqual(len(test_store._attribute_container_cache), 1) | ||
| def testGetCachedAttributeContainer(self): | ||
| """Tests the _GetCachedAttributeContainer function.""" | ||
| attribute_container = test_lib.TestAttributeContainer() | ||
| with test_lib.TempDirectory(): | ||
| test_store = interface.AttributeContainerStoreWithReadCache() | ||
| cached_container = test_store._GetCachedAttributeContainer( | ||
| attribute_container.CONTAINER_TYPE, 1) | ||
| self.assertIsNone(cached_container) | ||
| test_store._CacheAttributeContainerByIndex(attribute_container, 1) | ||
| cached_container = test_store._GetCachedAttributeContainer( | ||
| attribute_container.CONTAINER_TYPE, 1) | ||
| self.assertIsNotNone(cached_container) | ||
| if __name__ == '__main__': | ||
| unittest.main() |
+30
-32
@@ -119,14 +119,2 @@ #!/usr/bin/env python3 | ||
| def testCacheAttributeContainerByIndex(self): | ||
| """Tests the _CacheAttributeContainerByIndex function.""" | ||
| attribute_container = test_lib.TestAttributeContainer() | ||
| with test_lib.TempDirectory(): | ||
| test_store = sqlite_store.SQLiteAttributeContainerStore() | ||
| self.assertEqual(len(test_store._attribute_container_cache), 0) | ||
| test_store._CacheAttributeContainerByIndex(attribute_container, 0) | ||
| self.assertEqual(len(test_store._attribute_container_cache), 1) | ||
| def testCheckStorageMetadata(self): | ||
@@ -223,19 +211,33 @@ """Tests the _CheckStorageMetadata function.""" | ||
| def testGetCachedAttributeContainer(self): | ||
| """Tests the _GetCachedAttributeContainer function.""" | ||
| def testGetNumberOfAttributeContainerRows(self): | ||
| """Tests the _GetNumberOfAttributeContainerRows function.""" | ||
| attribute_container = test_lib.TestAttributeContainer() | ||
| with test_lib.TempDirectory(): | ||
| with test_lib.TempDirectory() as temp_directory: | ||
| test_path = os.path.join(temp_directory, 'acstore.sqlite') | ||
| test_store = sqlite_store.SQLiteAttributeContainerStore() | ||
| test_store.Open(path=test_path, read_only=False) | ||
| cached_container = test_store._GetCachedAttributeContainer( | ||
| attribute_container.CONTAINER_TYPE, 1) | ||
| self.assertIsNone(cached_container) | ||
| try: | ||
| number_of_containers = test_store._GetNumberOfAttributeContainerRows( | ||
| attribute_container.CONTAINER_TYPE) | ||
| self.assertEqual(number_of_containers, 0) | ||
| test_store._CacheAttributeContainerByIndex(attribute_container, 1) | ||
| test_store.AddAttributeContainer(attribute_container) | ||
| cached_container = test_store._GetCachedAttributeContainer( | ||
| attribute_container.CONTAINER_TYPE, 1) | ||
| self.assertIsNotNone(cached_container) | ||
| number_of_containers = test_store._GetNumberOfAttributeContainerRows( | ||
| attribute_container.CONTAINER_TYPE) | ||
| self.assertEqual(number_of_containers, 1) | ||
| # Test for a supported container type that does not have a table | ||
| # present in the storage file. | ||
| query = f'DROP TABLE {attribute_container.CONTAINER_TYPE:s}' | ||
| test_store._cursor.execute(query) | ||
| number_of_containers = test_store._GetNumberOfAttributeContainerRows( | ||
| attribute_container.CONTAINER_TYPE) | ||
| self.assertEqual(number_of_containers, 0) | ||
| finally: | ||
| test_store.Close() | ||
| def testHasTable(self): | ||
@@ -288,3 +290,3 @@ """Tests the _HasTable function.""" | ||
| try: | ||
| number_of_containers = test_store.GetNumberOfAttributeContainers( | ||
| number_of_containers = test_store._GetNumberOfAttributeContainerRows( | ||
| attribute_container.CONTAINER_TYPE) | ||
@@ -295,3 +297,3 @@ self.assertEqual(number_of_containers, 0) | ||
| number_of_containers = test_store.GetNumberOfAttributeContainers( | ||
| number_of_containers = test_store._GetNumberOfAttributeContainerRows( | ||
| attribute_container.CONTAINER_TYPE) | ||
@@ -302,3 +304,3 @@ self.assertEqual(number_of_containers, 1) | ||
| number_of_containers = test_store.GetNumberOfAttributeContainers( | ||
| number_of_containers = test_store._GetNumberOfAttributeContainerRows( | ||
| attribute_container.CONTAINER_TYPE) | ||
@@ -323,3 +325,3 @@ self.assertEqual(number_of_containers, 1) | ||
| try: | ||
| number_of_containers = test_store.GetNumberOfAttributeContainers( | ||
| number_of_containers = test_store._GetNumberOfAttributeContainerRows( | ||
| attribute_container.CONTAINER_TYPE) | ||
@@ -330,3 +332,3 @@ self.assertEqual(number_of_containers, 0) | ||
| number_of_containers = test_store.GetNumberOfAttributeContainers( | ||
| number_of_containers = test_store._GetNumberOfAttributeContainerRows( | ||
| attribute_container.CONTAINER_TYPE) | ||
@@ -478,8 +480,4 @@ self.assertEqual(number_of_containers, 1) | ||
| # Test for a supported container type that does not have a table | ||
| # present in the storage file. | ||
| query = f'DROP TABLE {attribute_container.CONTAINER_TYPE:s}' | ||
| test_store._cursor.execute(query) | ||
| number_of_containers = test_store.GetNumberOfAttributeContainers( | ||
| attribute_container.CONTAINER_TYPE) | ||
| 'bogus') | ||
| self.assertEqual(number_of_containers, 0) | ||
@@ -486,0 +484,0 @@ |
+43
-30
| [tox] | ||
| envlist = py3{7,8,9,10,11},coverage,docs,lint | ||
| envlist = py3{7,8,9,10,11,12},coverage,docformatter,docs,lint,wheel | ||
@@ -8,24 +8,36 @@ [testenv] | ||
| passenv = | ||
| CFLAGS | ||
| CPPFLAGS | ||
| LDFLAGS | ||
| CFLAGS | ||
| CPPFLAGS | ||
| LDFLAGS | ||
| setenv = | ||
| PYTHONPATH = {toxinidir} | ||
| PYTHONPATH = {toxinidir} | ||
| deps = | ||
| -rrequirements.txt | ||
| -rtest_requirements.txt | ||
| coverage: coverage | ||
| -rrequirements.txt | ||
| -rtest_requirements.txt | ||
| coverage: coverage | ||
| wheel: | ||
| build | ||
| setuptools >= 65 | ||
| wheel | ||
| commands = | ||
| py3{7,8,9,10,11}: ./run_tests.py | ||
| coverage: coverage erase | ||
| coverage: coverage run --source=acstore --omit="*_test*,*__init__*,*test_lib*" run_tests.py | ||
| coverage: coverage xml | ||
| py3{7,8,9,10,11,12}: ./run_tests.py | ||
| coverage: coverage erase | ||
| coverage: coverage run --source=acstore --omit="*_test*,*__init__*,*test_lib*" run_tests.py | ||
| coverage: coverage xml | ||
| wheel: python -m build --no-isolation --wheel | ||
| [testenv:docformatter] | ||
| usedevelop = True | ||
| deps = | ||
| docformatter | ||
| commands = | ||
| docformatter --in-place --recursive acstore tests | ||
| [testenv:docs] | ||
| usedevelop = True | ||
| deps = | ||
| -rdocs/requirements.txt | ||
| -rdocs/requirements.txt | ||
| commands = | ||
| sphinx-build -b html -d build/doctrees docs dist/docs | ||
| sphinx-build -b linkcheck docs dist/docs | ||
| sphinx-build -b html -d build/doctrees docs dist/docs | ||
| sphinx-build -b linkcheck docs dist/docs | ||
@@ -36,19 +48,20 @@ [testenv:lint] | ||
| passenv = | ||
| CFLAGS | ||
| CPPFLAGS | ||
| LDFLAGS | ||
| CFLAGS | ||
| CPPFLAGS | ||
| LDFLAGS | ||
| setenv = | ||
| PYTHONPATH = {toxinidir} | ||
| PYTHONPATH = {toxinidir} | ||
| deps = | ||
| -rrequirements.txt | ||
| -rtest_requirements.txt | ||
| pylint >= 2.17.0, < 2.18.0 | ||
| yamllint >= 1.26.0 | ||
| -rrequirements.txt | ||
| -rtest_requirements.txt | ||
| docformatter | ||
| pylint >= 3.0.0, < 3.1.0 | ||
| setuptools | ||
| yamllint >= 1.26.0 | ||
| commands = | ||
| pylint --version | ||
| yamllint -v | ||
| # Ignore setup.py for now due to: | ||
| # setup.py:15:0: E0001: Cannot import 'distutils.command.bdist_msi' due to | ||
| # syntax error 'expected an indented block (<unknown>, line 347)' (syntax-error) | ||
| pylint --rcfile=.pylintrc acstore tests | ||
| yamllint -c .yamllint.yaml test_data | ||
| docformatter --version | ||
| pylint --version | ||
| yamllint -v | ||
| docformatter --check --diff --recursive acstore setup.py tests | ||
| pylint --rcfile=.pylintrc acstore setup.py tests | ||
| yamllint -c .yamllint.yaml test_data |
@@ -15,2 +15,5 @@ #!/bin/bash | ||
| # Update the version in the setuptools configuration. | ||
| sed "s/version = [0-9]*/version = ${VERSION}/" -i setup.cfg | ||
| # Update the version in the dpkg configuration files. | ||
@@ -26,5 +29,5 @@ cat > config/dpkg/changelog << EOT | ||
| # Regenerate the API documentation. | ||
| tox -edocs | ||
| tox -edocformatter,docs | ||
| exit ${EXIT_SUCCESS}; | ||
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
206194
1.34%78
4%3332
-0.74%