From dbf39434d0419de64406b185ad3f2808089dbdac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Ma=C5=9Blanka?= <piotr.maslanka@ericsson.com> Date: Fri, 2 Aug 2024 10:56:06 +0200 Subject: [PATCH] fix --- .gitlab-ci.yml | 20 +++++++++++++++++++- README.md | 5 ++--- docs/usage.rst | 7 +++++++ tempsdb/chunks/base.pxd | 3 ++- tempsdb/chunks/base.pyx | 15 +++++++++++---- tempsdb/database.pyx | 5 ++++- tempsdb/series.pyx | 11 ++++++++--- 7 files changed, 53 insertions(+), 13 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0ec9352..d366216 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,9 +19,27 @@ stages: script: - python -m coverage run -m nose2 -vv -F - python -m coverage report - + coverage: /TOTAL.*\s+(\d+\%)/ test_python38: extends: .test_python image: python:3.8 + + + +test_python39: + extends: .test_python + image: python:3.9 + + + +test_python310: + extends: .test_python + image: python:3.10 + + + +test_python311: + extends: .test_python + image: python:3.11 diff --git a/README.md b/README.md index bb70449..3a26786 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,8 @@ [](https://badge.fury.io/py/tempsdb) [](https://pypi.python.org/pypi/tempsdb) [](http://tempsdb.readthedocs.io/en/latest/?badge=latest) -[](https://codeclimate.com/github/smok-serwis/tempsdb/maintainability) -[](https://codeclimate.com/github/smok-serwis/tempsdb/test_coverage) -[](https://travis-ci.com/smok-serwis/tempsdb) +[](https://git.dms-serwis.com.pl/smokserwis/tempsdb) +[](https://git.dms-serwis.com.pl/smokserwis/tempsdb/-/commits/develop) [](https://pypi.org/project/tempsdb/) [](https://github.com/smok-serwis/tempsdb) diff --git a/docs/usage.rst b/docs/usage.rst index 4c3876b..d8015cb 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -54,3 +54,10 @@ Logging tempsdb will log when opening and closing series. To prevent this from happening, just call: .. autofunction:: tempsdb.database.disable_logging + +Failing to close things +----------------------- + +Please be keen on manually closing things that are no longer necessary. +Most destructors check for that and will complain if you forget to manually +close anything. \ No newline at end of file diff --git a/tempsdb/chunks/base.pxd b/tempsdb/chunks/base.pxd index bd446f5..e2f3dd4 100644 --- a/tempsdb/chunks/base.pxd +++ b/tempsdb/chunks/base.pxd @@ -11,6 +11,7 @@ cdef class Chunk: cdef: TimeSeries parent readonly str path + bint is_object_closed readonly unsigned long long min_ts readonly unsigned long long max_ts readonly unsigned int block_size @@ -28,7 +29,7 @@ cdef class Chunk: cpdef bytes get_value_at(self, unsigned int index) cpdef bytes get_slice_of_piece_at(self, unsigned int index, unsigned int start, unsigned int stop) cpdef bytes get_slice_of_piece_starting_at(self, unsigned int index, unsigned int start) - cpdef int get_byte_of_piece(self, unsigned int index, unsigned int byte_index) except -1 + cpdef unsigned char get_byte_of_piece(self, unsigned int index, unsigned int byte_index) except -1 cpdef unsigned int find_left(self, unsigned long long timestamp) cpdef unsigned int find_right(self, unsigned long long timestamp) cdef object open_file(self, str path) diff --git a/tempsdb/chunks/base.pyx b/tempsdb/chunks/base.pyx index 5a90cb6..ce005d6 100644 --- a/tempsdb/chunks/base.pyx +++ b/tempsdb/chunks/base.pyx @@ -7,7 +7,7 @@ import mmap import warnings from .gzip cimport ReadWriteGzipFile -from ..exceptions import Corruption, StillOpen +from ..exceptions import Corruption, StillOpen, InvalidState from ..series cimport TimeSeries @@ -182,6 +182,7 @@ cdef class Chunk: self.page_size = page_size self.parent = parent self.closed = False + self.is_object_closed = False self.path = path self.file = self.open_file(path) self.file_lock_object = None @@ -213,7 +214,7 @@ cdef class Chunk: self.after_init() - cpdef int get_byte_of_piece(self, unsigned int index, unsigned int byte_index) except -1: + cpdef unsigned char get_byte_of_piece(self, unsigned int index, unsigned int byte_index) except -1: """ Return a particular byte of given element at given index. @@ -325,8 +326,12 @@ cdef class Chunk: cdef int sync(self) except -1: """ - Synchronize the mmap + Synchronize the mmap. + + :raises InvalidState: object is already closed """ + if self.is_object_closed: + raise InvalidState('Cannot sync a closed chunk!') self.mmap.flush() return 0 @@ -426,10 +431,12 @@ cdef class Chunk: self.sync() self.mmap.close() self.file.close() + self.closed = True + self.is_object_closed = True return 0 def __del__(self) -> None: - if self.closed: + if self.is_object_closed or self.closed: return warnings.warn('You forgot to close a Chunk') self.close() diff --git a/tempsdb/database.pyx b/tempsdb/database.pyx index d5b5495..f210b1f 100644 --- a/tempsdb/database.pyx +++ b/tempsdb/database.pyx @@ -27,6 +27,8 @@ cdef class Database: If you forget to, the destructor will do that instead and emit a warning. + This is internally synchronized thanks to a reentrant lock. + :param path: path to the directory with the database :raises DoesNotExist: database does not exist, use `create_database` @@ -35,6 +37,7 @@ cdef class Database: """ def __init__(self, str path): if not os.path.isdir(path): + self.closed = True raise DoesNotExist('Database does not exist') if not os.path.isdir(os.path.join(path, 'varlen')): @@ -393,6 +396,7 @@ cdef class Database: cdef: TimeSeries series VarlenSeries var_series + self.closed = True with self.lock: for series in self.open_series.values(): series.close() # because already closed series won't close themselves @@ -400,7 +404,6 @@ cdef class Database: for var_series in self.open_varlen_series.values(): var_series.close(True) self.open_varlen_series = {} - self.closed = True if self.mpm_handler is not None: self.mpm_handler.cancel() self.mpm_handler = None diff --git a/tempsdb/series.pyx b/tempsdb/series.pyx index 5f1603b..47ba142 100644 --- a/tempsdb/series.pyx +++ b/tempsdb/series.pyx @@ -111,6 +111,7 @@ cdef class TimeSeries: self.path = path if not os.path.isdir(self.path): + self.closed = True raise DoesNotExist('Chosen time series does not exist') cdef: @@ -130,6 +131,7 @@ cdef class TimeSeries: self.metadata = metadata.get('metadata') self.gzip_level = metadata.get('gzip_level', 0) except (OSError, ValueError, KeyError) as e: + self.closed = True raise Corruption('Corrupted series: %s' % (e, )) from e self.open_chunks = {} # tp.Dict[int, Chunk] @@ -137,6 +139,7 @@ cdef class TimeSeries: #: timestamp, is_direct, is_gzip if not len(files): + self.closed = True raise Corruption('Empty directory!') elif len(files) == 1: # empty series @@ -156,12 +159,14 @@ cdef class TimeSeries: try: self.chunks.append((int(filename), is_direct, is_gzip)) except ValueError: + self.closed = True raise Corruption('Detected invalid file "%s"' % (filename, )) self.chunks.sort() try: last_chunk_name, is_direct, is_gzip = self.chunks[-1] except IndexError as e: + self.closed = True raise Corruption('Corrupted series: %s' % (e, )) from e self.last_chunk = self.open_chunk(last_chunk_name, is_direct, is_gzip) self.last_entry_ts = self.last_chunk.max_ts @@ -274,11 +279,11 @@ cdef class TimeSeries: """ cdef: Chunk chunk - list open_chunks + list l_open_chunks if self.closed: return 0 - open_chunks = list(self.open_chunks.values()) - for chunk in open_chunks: + l_open_chunks = list(self.open_chunks.values()) + for chunk in l_open_chunks: chunk.close(True) if self.mpm is not None: self.mpm.cancel() -- GitLab