Merge branch 'master' into merge

This commit is contained in:
Thomas Waldmann 2015-04-21 21:02:42 +02:00
commit 354b3d34e3
8 changed files with 50 additions and 28 deletions

View file

@ -3,6 +3,14 @@ Attic Changelog
Here you can see the full list of changes between each Attic release.
Version 0.16
------------
(bugfix release, released on X)
- Fix "All archives" output for attic info. (#183)
- More user friendly error message when repository key file is not found (#236)
- Fix parsing of iso 8601 timestamps with zero microseconds (#282)
Version 0.15
------------

View file

@ -1,4 +1,4 @@
from datetime import datetime, timedelta, timezone
from datetime import datetime
from getpass import getuser
from itertools import groupby
import errno
@ -17,7 +17,7 @@ from attic import xattr
from attic.platform import acl_get, acl_set
from attic.chunker import Chunker
from attic.hashindex import ChunkIndex
from attic.helpers import Error, uid2user, user2uid, gid2group, group2gid, \
from attic.helpers import parse_timestamp, Error, uid2user, user2uid, gid2group, group2gid, \
Manifest, Statistics, decode_dict, st_mtime_ns, make_path_safe, StableDict, int_to_bigint, bigint_to_int
ITEMS_BUFFER = 1024 * 1024
@ -172,11 +172,7 @@ class Archive:
@property
def ts(self):
"""Timestamp of archive creation in UTC"""
t = self.metadata[b'time'].split('.', 1)
dt = datetime.strptime(t[0], '%Y-%m-%dT%H:%M:%S').replace(tzinfo=timezone.utc)
if len(t) > 1:
dt += timedelta(seconds=float('.' + t[1]))
return dt
return parse_timestamp(self.metadata[b'time'])
def __repr__(self):
return 'Archive(%r)' % self.name
@ -225,9 +221,9 @@ class Archive:
def calc_stats(self, cache):
def add(id):
count, size, csize = self.cache.chunks[id]
count, size, csize = cache.chunks[id]
stats.update(size, csize, count == 1)
self.cache.chunks[id] = count - 1, size, csize
cache.chunks[id] = count - 1, size, csize
def add_file_chunks(chunks):
for id, _, _ in chunks:
add(id)

View file

@ -103,11 +103,7 @@ class Cache:
os.remove(os.path.join(self.path, 'config')) # kill config first
shutil.rmtree(self.path)
def open(self):
if not os.path.isdir(self.path):
raise Exception('%s Does not look like an Attic cache' % self.path)
self.lock = UpgradableLock(os.path.join(self.path, 'config'), exclusive=True)
self.rollback()
def _do_open(self):
self.config = RawConfigParser()
self.config.read(os.path.join(self.path, 'config'))
if self.config.getint('cache', 'version') != 1:
@ -120,6 +116,12 @@ class Cache:
self.chunks = ChunkIndex.read(os.path.join(self.path, 'chunks').encode('utf-8'))
self.files = None
def open(self):
if not os.path.isdir(self.path):
raise Exception('%s Does not look like an Attic cache' % self.path)
self.lock = UpgradableLock(os.path.join(self.path, 'config'), exclusive=True)
self.rollback()
def close(self):
if self.lock:
self.lock.release()
@ -192,6 +194,7 @@ class Cache:
if os.path.exists(os.path.join(self.path, 'txn.tmp')):
shutil.rmtree(os.path.join(self.path, 'txn.tmp'))
self.txn_active = False
self._do_open()
def sync(self):
"""Initializes cache by fetching and reading all archive indicies

View file

@ -197,6 +197,14 @@ def to_localtime(ts):
return datetime(*time.localtime((ts - datetime(1970, 1, 1, tzinfo=timezone.utc)).total_seconds())[:6])
def parse_timestamp(timestamp):
"""Parse a ISO 8601 timestamp string"""
if '.' in timestamp: # microseconds might not be pressent
return datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%f').replace(tzinfo=timezone.utc)
else:
return datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S').replace(tzinfo=timezone.utc)
def update_excludes(args):
"""Merge exclude patterns from files with those on command line.
Empty lines and lines starting with '#' are ignored, but whitespace

View file

@ -17,6 +17,10 @@ class UnsupportedPayloadError(Error):
"""Unsupported payload type {}. A newer version is required to access this repository.
"""
class KeyfileNotFoundError(Error):
"""No key file for repository {} found in {}.
"""
class HMAC(hmac.HMAC):
"""Workaround a bug in Python < 3.4 Where HMAC does not accept memoryviews
@ -228,7 +232,7 @@ class KeyfileKey(AESKeyBase):
line = fd.readline().strip()
if line and line.startswith(cls.FILE_ID) and line[10:] == id:
return filename
raise Exception('Key file for repository with ID %s not found' % id)
raise KeyfileNotFoundError(repository._location.canonical_path(), get_keys_dir())
def load(self, filename, passphrase):
with open(filename, 'r') as fd:

View file

@ -1,12 +1,10 @@
import os
import sys
platform = os.uname()[0]
if platform == 'Linux':
if sys.platform.startswith('linux'):
from attic.platform_linux import acl_get, acl_set, API_VERSION
elif platform == 'FreeBSD':
elif sys.platform.startswith('freebsd'):
from attic.platform_freebsd import acl_get, acl_set, API_VERSION
elif platform == 'Darwin':
elif sys.platform == 'darwin':
from attic.platform_darwin import acl_get, acl_set, API_VERSION
else:
API_VERSION = 2

View file

@ -4,8 +4,8 @@ from datetime import datetime, timezone, timedelta
import os
import tempfile
import unittest
from attic.helpers import adjust_patterns, exclude_path, Location, format_timedelta, ExcludePattern, make_path_safe, UpgradableLock, prune_within, prune_split, \
StableDict, int_to_bigint, bigint_to_int
from attic.helpers import adjust_patterns, exclude_path, Location, format_timedelta, IncludePattern, ExcludePattern, make_path_safe, UpgradableLock, prune_within, prune_split, to_localtime, \
StableDict, int_to_bigint, bigint_to_int, parse_timestamp
from attic.testsuite import AtticTestCase
import msgpack
@ -209,3 +209,10 @@ class StableDictTestCase(AtticTestCase):
d = StableDict(foo=1, bar=2, boo=3, baz=4)
self.assert_equal(list(d.items()), [('bar', 2), ('baz', 4), ('boo', 3), ('foo', 1)])
self.assert_equal(hashlib.md5(msgpack.packb(d)).hexdigest(), 'fc78df42cd60691b3ac3dd2a2b39903f')
class TestParseTimestamp(AtticTestCase):
def test(self):
self.assert_equal(parse_timestamp('2015-04-19T20:25:00.226410'), datetime(2015, 4, 19, 20, 25, 0, 226410, timezone.utc))
self.assert_equal(parse_timestamp('2015-04-19T20:25:00'), datetime(2015, 4, 19, 20, 25, 0, 0, timezone.utc))

View file

@ -9,8 +9,6 @@ versioneer.versionfile_build = 'attic/_version.py'
versioneer.tag_prefix = ''
versioneer.parentdir_prefix = 'Attic-' # dirname like 'myproject-1.2.0'
platform = os.uname()[0]
min_python = (3, 2)
if sys.version_info < min_python:
print("Attic requires Python %d.%d or later" % min_python)
@ -89,11 +87,11 @@ ext_modules = [
Extension('attic.chunker', [chunker_source]),
Extension('attic.hashindex', [hashindex_source])
]
if platform == 'Linux':
if sys.platform.startswith('linux'):
ext_modules.append(Extension('attic.platform_linux', [platform_linux_source], libraries=['acl']))
elif platform == 'FreeBSD':
elif sys.platform.startswith('freebsd'):
ext_modules.append(Extension('attic.platform_freebsd', [platform_freebsd_source]))
elif platform == 'Darwin':
elif sys.platform == 'darwin':
ext_modules.append(Extension('attic.platform_darwin', [platform_darwin_source]))
setup(