Skip to content

Commit

Permalink
Merge branch 'release/v0.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
root committed Jul 17, 2017
2 parents 9232845 + 2f87c09 commit 60f73fa
Show file tree
Hide file tree
Showing 14 changed files with 416 additions and 1 deletion.
9 changes: 9 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[run]
parallel = true
branch = true
source = os_rotatefile

[paths]
source =
src/os_rotatefile
.tox/*/lib/python*/site-packages/os_rotatefile
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,6 @@ ENV/

# mypy
.mypy_cache/

# vscode
.vscode/
20 changes: 20 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
language: python
sudo: false
python:
- '2.7'
install:
- pip install -e .
- pip install pytest
- pip install coverage
- pip install codecov
script:
- coverage erase
- coverage run -m py.test
- coverage combine
after_success:
- codecov
deploy:
provider: pypi
user: cfhamlet
password:
secure: CepQGAyZE2tA7mwpAJ/I1dpxnZYGXN/fCCQZAh7ngVskYnGbnrvPu7EiZ8CcX1BL04vU6LzAesE3oNqGI9S2TXxChCocVM1tIG46pbkXdMGGQSscp2zVMTQAqZUXiRUDFDvTEjPRLD4JB/i0yJxQa5rlCzGAHPfXat3HLGS+SnAM64Ip9cO2dVdE4Q+u+Ungwnzv7ebnvSURrrmCT7ey57k02UKJsP1rRBFiec55R30ILVsvNOqWNmhwwgwVm7X3c5iUh9B55uDaFn0hEVyEsY6xGqCBlviVIymxQe/+32BMPc2JjKqrTTIUwd/+XrRbdAlp2jD8/SUY3wjz+PcZRaPjZ8AQ4w2EnofYghB/8eZ/8weM2dg2BAbTHpnrJkHG30Q62dSy2qFtlOjiv0SeJbV8W5Mfc3eW2AoaHBMjKl/RXEYF0nE6zqE+G9kVfEn8oD+fE3mUJ+4pAAN0IWhrJH8B9koUiIpTKfwrfGM7yp+peY0ygblKsZmkuGoniZa1f/3cBulhBk0HRJlSByvRj1LFyW+C9S1MGKk72k4mzG6JVVp3ioFyU7fwoyTNw+JBMZT9x7Kj5kFbMPZMBzhmN5Zq89NbKcEu3d01NSzhBfFC/rm/DPvZEsaFFicTqBwICcRUQPKHaHXTPkA9uRA7fyJ6mdyZLHIluwEgXGWIbQA=
6 changes: 6 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
include LICENSE
include README.md
include MANIFEST.in
include src/os_rotatefile/VERSION
recursive-include tests *.py

12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
# os-rotatefile
For reading and writing files which rotate by size.
Reading and writing size rotate file.

# Install
`pip install os-rotatefile`

# Unit Tests
`$ tox`

# License
MIT licensed.

5 changes: 5 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[pytest]
addopts = -s --fulltrace
env =
COVERAGE = true

2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[metadata]
description-file = README.md
36 changes: 36 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from setuptools import setup, find_packages


def read(*filenames, **kwargs):
import io
from os.path import join, dirname
encoding = kwargs.get('encoding', 'utf-8')
sep = kwargs.get('sep', '\n')
buf = []
for filename in filenames:
with io.open(join(dirname(__file__), filename), encoding=encoding) as f:
buf.append(f.read())
return sep.join(buf)


setup(
name='os-rotatefile',
version=read('src/os_rotatefile/VERSION'),
packages=find_packages(where='src'),
package_dir={'': 'src'},
include_package_data=True,
license='MIT License',
description='Reading and writing size rotate file.',
long_description=open('README.md').read(),
author='Ozzy',
author_email='cfhamlet@gmail.com',
url='https://github.com/cfhamlet/os-ratatefile',
zip_safe=False,
install_requires=['six>=1.5.2',],
classifiers=[
'Development Status :: 2 - Pre-Alpha',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Natural Language :: English',
'Programming Language :: Python :: 2.7',
])
1 change: 1 addition & 0 deletions src/os_rotatefile/VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.1.0
9 changes: 9 additions & 0 deletions src/os_rotatefile/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import sys
from rotatefile import open_file
__all__ = ['__version__', 'version_info', 'open_file']

import pkgutil
__version__ = pkgutil.get_data(__package__, 'VERSION').decode('ascii').strip()
version_info = tuple(int(v) if v.isdigit() else v
for v in __version__.split('.'))
del pkgutil
184 changes: 184 additions & 0 deletions src/os_rotatefile/rotatefile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import os
import six
import sys
import StringIO


def _complain_ifclosed(closed):
if closed:
raise ValueError, "I/O operation on closed file"


class RotateBase(object):
def __init__(self, base_filename):
self._prefix = os.path.basename(base_filename)
self._path = os.path.abspath(os.path.dirname(base_filename))
self._fp = None
self._idx = -1
self.closed = True

def _get_filename(self, idx):
if idx < 0:
raise ValueError, 'idx must >= 0'
return os.path.join(self._path, '%s%d' % (self._prefix, idx))

def close(self):
if self.closed:
return
if self._fp is not None:
self._fp.close()
self._fp = None
self.closed = True


class RotateReader(RotateBase):
def __init__(self, base_filename):
super(RotateReader, self).__init__(base_filename)
self._idx = None
self._end = False
self._open_next()

def _open_next(self):
if self._end:
return
if self._idx is None:
self._idx = sys.maxint
has_min = False
for x in [x for x in os.listdir(self._path) if x.startswith(self._prefix)]:
try:
self._idx = min(
self._idx, int(x[len(self._prefix):]))
has_min = True
except:
continue
if has_min and self._idx >= 0:
self._idx += -1

self._idx += 1
filename = self._get_filename(self._idx)
self._fp = open(filename, "r")
self.closed = False

def read(self, size):
_complain_ifclosed(self.closed)
assert size >= 0, 'size must >= 0'
if self._end or size == 0:
return ''
buffer = StringIO.StringIO()
need = size
while buffer.len < size:
data = self._fp.read(need)
if not data:
try:
fp = self._fp
self._open_next()
fp.close()
continue
except IOError:
self._end = True
break

buffer.write(data)
need = size - buffer.len

buffer.seek(0)
return buffer.read()

def readline(self):
_complain_ifclosed(self.closed)
if self._end:
return ''
buffer = StringIO.StringIO()
e = ''
while e != '\n':
line = self._fp.readline()
if not line:
try:
fp = self._fp
self._open_next()
fp.close
except IOError:
self._end = True
break
buffer.write(line)
buffer.seek(-1, 1)
e = buffer.read(1)
buffer.seek(0)
return buffer.read()


class RotateWriter(RotateBase):
def __init__(self, base_filename, roll_size='1G'):
super(RotateWriter, self).__init__(base_filename)
self._roll_size = valid_roll_size(roll_size)
assert self._roll_size > 0, 'roll_size must > 0'
self._size = -1
self._open_next()

def _open_next(self):
if not os.path.exists(self._path):
os.makedirs(self._path)

if self._idx < 0:
for x in [x for x in os.listdir(self._path) if x.startswith(self._prefix)]:
try:
self._idx = max(
self._idx, int(x[len(self._prefix):]))
except:
continue

if self._idx >= 0:
filename = self._get_filename(self._idx)
size = os.path.getsize(filename)
if size < self._roll_size:
self._idx += -1

self._idx += 1
filename = self._get_filename(self._idx)
self._fp = open(filename, "a")
self._size = os.path.getsize(filename)
self.closed = False

def write(self, data, flush=False):
_complain_ifclosed(self.closed)
if self._fp is None:
self._open_next()
while True:
can_write = self._roll_size - self._size
if len(data) <= can_write:
self._fp.write(data)
if flush:
self._fp.flush()
self._size += len(data)
break
elif can_write > 0:
self._fp.write(data[:can_write])
self._size += can_write
self.close()
self._open_next()
data = data[can_write:]
else:
self.close()
self._open_next()


def open_file(name, mode='r', **kwargs):
def not_support(name, **kwargs):
raise ValueError, "mode must be 'r' or 'w'"
c = {'w': RotateWriter, 'r': RotateReader}.get(mode, not_support)
return c(name, **kwargs)


def valid_roll_size(roll_size):
if not isinstance(roll_size, (six.types.IntType, six.types.StringType)) or isinstance(roll_size, six.types.BooleanType):
raise TypeError, 'roll_size must be int or string type'
if isinstance(roll_size, six.types.IntType):
roll_size = int(roll_size)
return roll_size
roll_size = roll_size.lower()
multi = 1
for idx, x in enumerate(["k", "m", "g"]):
if x == roll_size[-1]:
roll_size = roll_size[:-1]
multi = pow(1024, idx + 1)
return int(float(roll_size) * multi)
Empty file added tests/__init__.py
Empty file.

0 comments on commit 60f73fa

Please sign in to comment.