diff --git a/.dockerignore b/.dockerignore index eb2762e9d02a4f5f7e09170a16017dbfc72df62b..677f96716c93178cfa0a7ac6d5e734e0a6b5c50a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,6 @@ .git .circleci docs +build +dist +*.egg-info diff --git a/LICENSE b/LICENSE index 90f6d377a5a402925429c4210e6e8aa5704fec93..1915d01c9ad047c2c1b8ac964b794e3130469016 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 SMOK +Copyright (c) 2020 SMOK sp. z o. o. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/docs/conf.py b/docs/conf.py index 194df61ba14bfc31289cda0eaa16c94c31afbfa0..c9865de954540efdce3caa24e521fe9ebcc3ba02 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -27,8 +27,8 @@ author = 'Piotr MaĹlanka' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [ -] +extensions = ['sphinx.ext.autodoc'] + # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -49,4 +49,4 @@ html_theme = 'alabaster' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] \ No newline at end of file +html_static_path = ['_static'] diff --git a/setup.py b/setup.py index 4bfc77d89d856a93a8887bd4e44ebaef2e33cc81..bb82f047c69c3c87905017374eeda21dd9849be9 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,6 @@ import os import typing as tp from satella.files import find_files -from setuptools import find_packages from distutils.core import setup from snakehouse import Multibuild, build @@ -12,11 +11,13 @@ def find_pyx(*path) -> tp.List[str]: setup(name='tempsdb', version='0.1_a1', - packages=find_packages(include=['tempsdb', 'tempsdb.*']), + packages=['tempsdb'], install_requires=['satella', 'ujson'], ext_modules=build([Multibuild('tempsdb', find_pyx('tempsdb')), ], compiler_directives={ 'language_level': '3', }), - python_requires='!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*' + python_requires='!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*', + test_suite="tests", + zip_safe=False ) diff --git a/tempsdb/__init__.py b/tempsdb/__init__.py index 93a0b2e8f779eb4b71867716e1321d66782a5d9b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/tempsdb/__init__.py +++ b/tempsdb/__init__.py @@ -1,2 +0,0 @@ -from tempsdb.__bootstrap__ import bootstrap_cython_submodules -bootstrap_cython_submodules() diff --git a/tempsdb/chunks.pyx b/tempsdb/chunks.pyx index 524f5a6b00e0b3ca03eb55683f326ec8c9db4c74..a2389a5136a187628a8f4be0d8ea39a2e62ee596 100644 --- a/tempsdb/chunks.pyx +++ b/tempsdb/chunks.pyx @@ -10,7 +10,9 @@ STRUCT_Q = struct.Struct('>Q') cdef class Chunk: """ - Represents a single chunk of time series + Represents a single chunk of time series. + + This also implements an iterator interface. This will iterate with tp.Tuple[int, bytes]. :param path: path to the chunk file :type path: str @@ -23,19 +25,29 @@ cdef class Chunk: """ def __init__(self, path: str): self.closed = False + cdef unsigned long long file_size = os.path.getsize(path) self.path = path cdef bytes b - self.file = open(self.path, 'rb') + print('Before open') + self.file = open(self.path, 'rb+') try: - self.mmap = mmap.mmap(self.file.fileno(), 0) - except OSError: - raise Corruption('Empty chunk file!') + self.mmap = mmap.mmap(self.file.fileno(), file_size, access=mmap.ACCESS_READ) + except OSError as e: + raise Corruption(f'Empty chunk file!') try: - self.min_ts, self.max_ts, self.block_size = STRUCT_QQL.unpack(self.file.read(16)) + self.min_ts, self.max_ts, self.block_size = STRUCT_QQL.unpack(self.mmap[:20]) except struct.error: raise Corruption('Could not read the header of the chunk file %s' % (self.path, )) - self.pointer = 8 - self.entries = (os.path.getsize(self.path)-20) / self.block_size + self.pointer = 20 + self.entries = (file_size-self.pointer) // self.block_size + + def __iter__(self): + cdef unsigned long i = 0 + for i in range(self.entries): + yield self.get_piece_at(i) + + def __len__(self): + return self.entries cpdef void close(self): """ @@ -59,9 +71,8 @@ cdef class Chunk: if index >= self.entries: raise IndexError('Index too large') cdef: - unsigned long starting_index = 20 + index * self.block_size - unsigned long stopping_index = starting_index + self.block_size - bytes bytes_at = self.mmap[starting_index:stopping_index] + unsigned long starting_index = 20 + index * (self.block_size+8) + unsigned long stopping_index = starting_index + self.block_size+8 unsigned long long ts = STRUCT_Q.unpack(self.mmap[starting_index:starting_index+8])[0] return ts, self.mmap[starting_index+8:stopping_index] @@ -107,10 +118,10 @@ cpdef Chunk create_chunk(str path, list data): file.write(b) last_ts = ts first_element = False - file.close() except ValueError: file.close() os.unlink(path) raise + file.close() return Chunk(path) diff --git a/tempsdb/series.pyx b/tempsdb/series.pyx index d87eba5bd2cf72517f9f2d5bce441f49efebb112..7d0c20209f5266d551934a34f55241192a9c7ede 100644 --- a/tempsdb/series.pyx +++ b/tempsdb/series.pyx @@ -1,10 +1,9 @@ import threading import time - import ujson from satella.files import read_in_file -from .chunks cimport Chunk, create_chunk +from .chunks cimport create_chunk from .database cimport Database from .exceptions import DoesNotExist, Corruption import os diff --git a/tests/test_db.py b/tests/test_db.py index 56413578c53be2d16df3ea72c7729c2183273795..12f678cd50bb8da374779ce12e7cbbe532be970b 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -1,15 +1,14 @@ import unittest +from tempsdb.chunks import create_chunk class TestDB(unittest.TestCase): def test_chunk(self): - from tempsdb.chunks import create_chunk - data = [(0, b'ala '), (1, b'ma '), (4, b'kota')] chunk = create_chunk('chunk.db', data) self.assertEqual(chunk.min_ts, 0) self.assertEqual(chunk.max_ts, 4) - self.assertEqual(chunk.bs, 4) + self.assertEqual(chunk.block_size, 4) self.assertEqual(chunk.get_piece_at(0), (0, b'ala ')) - self.assertEqual(chunk.get_piece_at(1), (1, b'ma ')) + self.assertEqual(chunk.get_piece_at(1), (1, b'ma ')) self.assertEqual(chunk.get_piece_at(2), (4, b'kota')) diff --git a/unittest.Dockerfile b/unittest.Dockerfile index cb64eb186826be299fcfd363890535d90601d8c4..610937e4c063e23c06aef87c709bc26b76ca20dc 100644 --- a/unittest.Dockerfile +++ b/unittest.Dockerfile @@ -1,8 +1,9 @@ FROM python:3.8 -RUN pip install satella snakehouse nose2 +RUN pip install satella snakehouse nose2 wheel ADD . /app WORKDIR /app +RUN python setup.py build_ext --inplace -CMD ["python", "setup.py", "test"] +CMD ["nose2", "-vv"]