10000 Pull in _NodeBase from actorgraph; include short-hash in repr · Meshinfo/ffmpeg-python@fc946be · GitHub
[go: up one dir, main page]

Skip to content

Commit fc946be

Browse files
committed
Pull in _NodeBase from actorgraph; include short-hash in repr
1 parent dcebe91 commit fc946be

File tree

2 files changed

+80
-18
lines changed

2 files changed

+80
-18
lines changed

ffmpeg/nodes.py

Lines changed: 74 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,72 @@
11
from __future__ import unicode_literals
22

33
from builtins import object
4+
import copy
45
import hashlib
5-
import json
66

77

8-
class Node(object):
9-
"""Node base"""
10-
def __init__(self, parents, name, *args, **kwargs):
8+
def _recursive_repr(item):
9+
"""Hack around python `repr` to deterministically represent dictionaries.
10+
11+
This is able to represent more things than json.dumps, since it does not require things to be JSON serializable
12+
(e.g. datetimes).
13+
"""
14+
if isinstance(item, basestring):
15+
result = str(item)
16+
elif isinstance(item, list):
17+
result = '[{}]'.format(', '.join([_recursive_repr(x) for x in item]))
18+
elif isinstance(item, dict):
19+
kv_pairs = ['{}: {}'.format(_recursive_repr(k), _recursive_repr(item[k])) for k in sorted(item)]
20+
result = '{' + ', '.join(kv_pairs) + '}'
21+
else:
22+
result = repr(item)
23+
return result
24+
25+
26+
def _create_hash(item):
27+
hasher = hashlib.sha224()
28+
repr_ = _recursive_repr(item)
29+
hasher.update(repr_.encode('utf-8'))
30+
return hasher.hexdigest()
31+
32+
33+
class _NodeBase(object):
34+
@property
35+
def hash(self):
36+
if self._hash is None:
37+
self._update_hash()
38+
return self._hash
39+
40+
def __init__(self, parents, name):
1141
parent_hashes = [hash(parent) for parent in parents]
1242
assert len(parent_hashes) == len(set(parent_hashes)), 'Same node cannot be included as parent multiple times'
1343
self._parents = parents
1444
self._hash = None
1545
self._name = name
16-
self._args = args
17-
self._kwargs = kwargs
46+
47+
def _transplant(self, new_parents):
48+
other = copy.copy(self)
49+
other._parents = copy.copy(new_parents)
50+
return other
51+
52+
@property
53+
def _repr_args(self):
54+
raise NotImplementedError()
55+
56+
@property
57+
def _repr_kwargs(self):
58+
raise NotImplementedError()
59+
60+
@property
61+
def _short_hash(self):
62+
return '{:x}'.format(abs(hash(self)))[:12]
1863

1964
def __repr__(self):
20-
formatted_props = ['{}'.format(arg) for arg in self._args]
21-
formatted_props += ['{}={!r}'.format(key, self._kwargs[key]) for key in sorted(self._kwargs)]
22-
return '{}({})'.format(self._name, ','.join(formatted_props))
65+
args = self._repr_args
66+
kwargs = self._repr_kwargs
67+
formatted_props = ['{!r}'.format(arg) for arg in args]
68+
formatted_props += ['{}={!r}'.format(key, kwargs[key]) for key in sorted(kwargs)]
69+
return '{}({}) <{}>'.format(self._name, ', '.join(formatted_props), self._short_hash)
2370

2471
def __hash__(self):
2572
if self._hash is None:
@@ -30,16 +77,31 @@ def __eq__(self, other):
3077
return hash(self) == hash(other)
3178

3279
def _update_hash(self):
33-
props = {'args': self._args, 'kwargs': self._kwargs}
34-
props_str = json.dumps(props, sort_keys=True).encode('utf-8')
35-
my_hash = hashlib.md5(props_str).hexdigest()
80+
props = {'args': self._repr_args, 'kwargs': self._repr_kwargs}
81+
my_hash = _create_hash(props)
3682
parent_hashes = [str(hash(parent)) for parent in self._parents]
3783
hashes = parent_hashes + [my_hash]
3884
hashes_str = ','.join(hashes).encode('utf-8')
3985
hash_str = hashlib.md5(hashes_str).hexdigest()
4086
self._hash = int(hash_str, base=16)
4187

4288

89+
class Node(_NodeBase):
90+
"""Node base"""
91+
def __init__(self, parents, name, *args, **kwargs):
92+
super(Node, self).__init__(parents, name)
93+
self._args = args
94+
self._kwargs = kwargs
95+
96+
@property
97+
def _repr_args(self):
98+
return self._args
99+
100+
@property
101+
def _repr_kwargs(self):
102+
return self._kwargs
103+
104+
43105
class InputNode(Node):
44106
"""InputNode type"""
45107
def __init__(self, name, *args, **kwargs):

ffmpeg/tests/test_ffmpeg.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,12 @@ def test_repr():
7373
trim3 = ffmpeg.trim(in_file, start_frame=50, end_frame=60)
7474
concatted = ffmpeg.concat(trim1, trim2, trim3)
7575
output = ffmpeg.output(concatted, 'dummy2.mp4')
76-
assert repr(in_file) == "input(filename={!r})".format('dummy.mp4')
77-
assert repr(trim1) == "trim(end_frame=20,start_frame=10)"
78-
assert repr(trim2) == "trim(end_frame=40,start_frame=30)"
79-
assert repr(trim3) == "trim(end_frame=60,start_frame=50)"
80-
assert repr(concatted) == "concat(n=3)"
81-
assert repr(output) == "output(filename={!r})".format('dummy2.mp4')
76+
assert repr(in_file) == "input(filename={!r}) <{}>".format('dummy.mp4', in_file._short_hash)
77+
assert repr(trim1) == "trim(end_frame=20, start_frame=10) <{}>".format(trim1._short_hash)
78+
assert repr(trim2) == "trim(end_frame=40, start_frame=30) <{}>".format(trim2._short_hash)
79+
assert repr(trim3) == "trim(end_frame=60, start_frame=50) <{}>".format(trim3._short_hash)
80+
assert repr(concatted) == "concat(n=3) <{}>".format(concatted._short_hash)
81+
assert repr(output) == "output(filename={!r}) <{}>".format('dummy2.mp4', output._short_hash)
8282

8383

8484
def test_get_args_simple():

0 commit comments

Comments
 (0)
0