dictdumper 0.8.4.post3__pp38-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dictdumper/__init__.py +52 -0
- dictdumper/_dateutil.py +79 -0
- dictdumper/_hexlify.py +11 -0
- dictdumper/_types.py +11 -0
- dictdumper/dumper.py +249 -0
- dictdumper/json.py +349 -0
- dictdumper/plist.py +319 -0
- dictdumper/tree.py +428 -0
- dictdumper/vuejs.py +128 -0
- dictdumper/xml.py +102 -0
- dictdumper-0.8.4.post3.dist-info/LICENSE +29 -0
- dictdumper-0.8.4.post3.dist-info/METADATA +245 -0
- dictdumper-0.8.4.post3.dist-info/RECORD +15 -0
- dictdumper-0.8.4.post3.dist-info/WHEEL +6 -0
- dictdumper-0.8.4.post3.dist-info/top_level.txt +1 -0
dictdumper/__init__.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Stream formatted output dumper.
|
|
3
|
+
|
|
4
|
+
:mod:`dictdumper` is an open source Python program works as a
|
|
5
|
+
stream formatted output dumper. Currently, it supports
|
|
6
|
+
following formats:
|
|
7
|
+
|
|
8
|
+
- :class:`~dictdumper.dumper.Dumper`
|
|
9
|
+
|
|
10
|
+
Abstract base class of all dumpers.
|
|
11
|
+
|
|
12
|
+
- :class:`~dictdumper.json.JSON`
|
|
13
|
+
|
|
14
|
+
Dump JavaScript object notation (``JSON``) format file.
|
|
15
|
+
|
|
16
|
+
- :class:`~dictdumper.plist.PLIST`
|
|
17
|
+
|
|
18
|
+
Dump Apple property list (``PLIST``) format file.
|
|
19
|
+
|
|
20
|
+
- :class:`~dictdumper.tree.Tree`
|
|
21
|
+
|
|
22
|
+
Dump tree-view text (``TXT``) format file.
|
|
23
|
+
|
|
24
|
+
- :class:`~dictdumper.xml.XML`
|
|
25
|
+
|
|
26
|
+
Dump extensible markup language (``XML``) file;
|
|
27
|
+
this is an abstract base class
|
|
28
|
+
|
|
29
|
+
- :class:`~dictdumper.vuejs.VueJS`
|
|
30
|
+
|
|
31
|
+
Dump JavaScript file using ``Vue.js`` framework;
|
|
32
|
+
this class is deprecated due to grammar error.
|
|
33
|
+
|
|
34
|
+
.. deprecated:: 0.8.0
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
# Base Class for DictDumper
|
|
38
|
+
from dictdumper.dumper import Dumper # pylint: disable=unused-import
|
|
39
|
+
from dictdumper.xml import XML # pylint: disable=unused-import
|
|
40
|
+
|
|
41
|
+
# Utility Classes
|
|
42
|
+
from dictdumper.json import JSON
|
|
43
|
+
from dictdumper.plist import PLIST
|
|
44
|
+
from dictdumper.tree import Tree
|
|
45
|
+
|
|
46
|
+
# Deprecated Classes
|
|
47
|
+
from dictdumper.vuejs import VueJS # pylint: disable=unused-import
|
|
48
|
+
|
|
49
|
+
__all__ = ['JSON', 'PLIST', 'Tree']
|
|
50
|
+
|
|
51
|
+
# version string
|
|
52
|
+
__version__ = '0.8.4.post3'
|
dictdumper/_dateutil.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Date & time processing utilities."""
|
|
3
|
+
|
|
4
|
+
import datetime
|
|
5
|
+
|
|
6
|
+
__all__ = ['isoformat']
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from datetime import _format_time
|
|
10
|
+
except ImportError:
|
|
11
|
+
def _format_time(hh, mm, ss, us, timespec='auto'):
|
|
12
|
+
specs = {
|
|
13
|
+
'hours': '{:02d}',
|
|
14
|
+
'minutes': '{:02d}:{:02d}',
|
|
15
|
+
'seconds': '{:02d}:{:02d}:{:02d}',
|
|
16
|
+
'milliseconds': '{:02d}:{:02d}:{:02d}.{:03d}',
|
|
17
|
+
'microseconds': '{:02d}:{:02d}:{:02d}.{:06d}'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if timespec == 'auto':
|
|
21
|
+
# Skip trailing microseconds when us==0.
|
|
22
|
+
timespec = 'microseconds' if us else 'seconds'
|
|
23
|
+
elif timespec == 'milliseconds':
|
|
24
|
+
us //= 1000
|
|
25
|
+
try:
|
|
26
|
+
fmt = specs[timespec]
|
|
27
|
+
except KeyError:
|
|
28
|
+
raise ValueError('Unknown timespec value')
|
|
29
|
+
else:
|
|
30
|
+
return fmt.format(hh, mm, ss, us)
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
from datetime import _format_offset
|
|
34
|
+
except ImportError:
|
|
35
|
+
def _format_offset(off):
|
|
36
|
+
s = ''
|
|
37
|
+
if off is not None:
|
|
38
|
+
if off.days < 0:
|
|
39
|
+
sign = "-"
|
|
40
|
+
off = -off
|
|
41
|
+
else:
|
|
42
|
+
sign = "+"
|
|
43
|
+
hh, mm = divmod(off, datetime.timedelta(hours=1))
|
|
44
|
+
mm, ss = divmod(mm, datetime.timedelta(minutes=1))
|
|
45
|
+
s += "%s%02d:%02d" % (sign, hh, mm)
|
|
46
|
+
if ss or ss.microseconds:
|
|
47
|
+
s += ":%02d" % ss.seconds
|
|
48
|
+
|
|
49
|
+
if ss.microseconds:
|
|
50
|
+
s += '.%06d' % ss.microseconds
|
|
51
|
+
return s
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def isoformat(date):
|
|
55
|
+
"""Generate ISO format date string."""
|
|
56
|
+
if hasattr(date, 'isoformat'):
|
|
57
|
+
return date.isoformat()
|
|
58
|
+
if isinstance(date, datetime.date):
|
|
59
|
+
return "%04d-%02d-%02d" % (date.year, date.month, date.day)
|
|
60
|
+
if isinstance(date, datetime.time):
|
|
61
|
+
s = _format_time(date.hour, date.minute, date.second,
|
|
62
|
+
date.microsecond, 'auto')
|
|
63
|
+
off = date.utcoffset()
|
|
64
|
+
tz = _format_offset(off)
|
|
65
|
+
if tz:
|
|
66
|
+
s += tz
|
|
67
|
+
return s
|
|
68
|
+
if isinstance(date, datetime.datetime):
|
|
69
|
+
s = ("%04d-%02d-%02d%c" % (date.year, date.month, date.day, 'T') +
|
|
70
|
+
_format_time(date.hour, date.minute, date.second,
|
|
71
|
+
date.microsecond, 'auto'))
|
|
72
|
+
|
|
73
|
+
off = date.utcoffset()
|
|
74
|
+
tz = _format_offset(off)
|
|
75
|
+
if tz:
|
|
76
|
+
s += tz
|
|
77
|
+
|
|
78
|
+
return s
|
|
79
|
+
raise NotImplementedError('unknown date object: %s' % date)
|
dictdumper/_hexlify.py
ADDED
dictdumper/_types.py
ADDED
dictdumper/dumper.py
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""base dumper
|
|
3
|
+
|
|
4
|
+
:mod:`~dictdumper.dumper` contains :class:`~dictdumper.dumper.Dumper` only,
|
|
5
|
+
which is an abstract base class for all dumpers, eg. :class:`~dictdumper.vuejs.VueJS`,
|
|
6
|
+
:class:`~dictdumper.json.JSON`, :class:`~dictdumper.plist.PLIST`,
|
|
7
|
+
:class:`~dictdumper.tree.Tree`, and :class:`~dictdumper.xml.XML`.
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
# Abstract Base Class of Dumpers
|
|
11
|
+
# Pre-define useful arguments and methods of dumpers
|
|
12
|
+
|
|
13
|
+
import abc
|
|
14
|
+
import collections
|
|
15
|
+
import os
|
|
16
|
+
import warnings
|
|
17
|
+
|
|
18
|
+
from dictdumper._types import str_type
|
|
19
|
+
|
|
20
|
+
__all__ = ['Dumper']
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def deprecated(cls):
|
|
24
|
+
"""Deprecation warning.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
cls (object): the class to mark as deprecated
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
object: The original ``cls`` itself.
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
warnings.warn('%s is deprecated' % cls.__name__, DeprecationWarning, stacklevel=2)
|
|
34
|
+
return cls
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class DumperError(TypeError):
|
|
38
|
+
"""Unsupported content type."""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Dumper(object): # pylint: disable=metaclass-assignment,useless-object-inheritance
|
|
42
|
+
"""Abstract base class of all dumpers.
|
|
43
|
+
|
|
44
|
+
.. code:: python
|
|
45
|
+
|
|
46
|
+
>>> dumper = Dumper(file_name)
|
|
47
|
+
>>> dumper(content_dict_1, name=content_name_1)
|
|
48
|
+
>>> dumper(content_dict_2, name=content_name_2)
|
|
49
|
+
............
|
|
50
|
+
|
|
51
|
+
Attributes:
|
|
52
|
+
_file (str): output file name
|
|
53
|
+
_sptr (int): indicates start of appending point (file pointer)
|
|
54
|
+
_tctr (int): tab level counter
|
|
55
|
+
_hsrt (str): start string (``_HEADER_START``)
|
|
56
|
+
_hend (str): end string (``_HEADER_END``)
|
|
57
|
+
|
|
58
|
+
"""
|
|
59
|
+
__metaclass__ = abc.ABCMeta
|
|
60
|
+
|
|
61
|
+
##########################################################################
|
|
62
|
+
# Properties.
|
|
63
|
+
##########################################################################
|
|
64
|
+
|
|
65
|
+
# file format of current dumper
|
|
66
|
+
@property
|
|
67
|
+
@abc.abstractmethod
|
|
68
|
+
def kind(self):
|
|
69
|
+
"""File format of current dumper.
|
|
70
|
+
|
|
71
|
+
:rtype: str
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def filename(self):
|
|
76
|
+
"""Output file name.
|
|
77
|
+
|
|
78
|
+
:rtype: str
|
|
79
|
+
"""
|
|
80
|
+
return self._file
|
|
81
|
+
|
|
82
|
+
##########################################################################
|
|
83
|
+
# Type codes.
|
|
84
|
+
##########################################################################
|
|
85
|
+
|
|
86
|
+
#: Tuple[Tuple[type, str]]: Type codes.
|
|
87
|
+
__type__ = tuple()
|
|
88
|
+
|
|
89
|
+
##########################################################################
|
|
90
|
+
# Methods.
|
|
91
|
+
##########################################################################
|
|
92
|
+
|
|
93
|
+
@staticmethod
|
|
94
|
+
def make_object(o, value, **kwargs):
|
|
95
|
+
"""Create an object with convertion information.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
o (Any): object to convert
|
|
99
|
+
value (Any): converted value of ``o``
|
|
100
|
+
**kwargs: additional information for the convertion
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Dict[str, Any]: Information context of the convertion.
|
|
104
|
+
|
|
105
|
+
"""
|
|
106
|
+
obj = collections.OrderedDict()
|
|
107
|
+
obj['type'] = str_type(type(o).__name__)
|
|
108
|
+
obj['value'] = value
|
|
109
|
+
for key, val in kwargs.items():
|
|
110
|
+
obj[key] = val
|
|
111
|
+
return obj
|
|
112
|
+
|
|
113
|
+
def object_hook(self, o): # pylint: disable=unused-argument,no-self-use
|
|
114
|
+
"""Convert content for function call.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
o (Any): object to convert
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Any: the converted object
|
|
121
|
+
|
|
122
|
+
"""
|
|
123
|
+
return o
|
|
124
|
+
|
|
125
|
+
def default(self, o): # pylint: disable=unused-argument,no-self-use
|
|
126
|
+
"""Check content type for function call.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
o (Any): object to check
|
|
130
|
+
|
|
131
|
+
Raises:
|
|
132
|
+
DumperError: ``o`` is an unsupported content type
|
|
133
|
+
|
|
134
|
+
"""
|
|
135
|
+
raise DumperError('unsupported content type: %s' % type(o).__name__)
|
|
136
|
+
|
|
137
|
+
##########################################################################
|
|
138
|
+
# Attributes.
|
|
139
|
+
##########################################################################
|
|
140
|
+
|
|
141
|
+
#: :obj:`int`, file pointer: Indicates start of appending point.
|
|
142
|
+
_sptr = os.SEEK_SET # seek pointer
|
|
143
|
+
#: :obj:`int`: Tab level counter.
|
|
144
|
+
_tctr = 1 # counter for tab level
|
|
145
|
+
|
|
146
|
+
#: Dumper head string.
|
|
147
|
+
_hsrt = ''
|
|
148
|
+
#: Dumper tail string.
|
|
149
|
+
_hend = ''
|
|
150
|
+
|
|
151
|
+
##########################################################################
|
|
152
|
+
# Data models.
|
|
153
|
+
##########################################################################
|
|
154
|
+
|
|
155
|
+
def __new__(cls, fname, **kwargs): # pylint: disable=unused-argument
|
|
156
|
+
self = super(Dumper, cls).__new__(cls)
|
|
157
|
+
return self
|
|
158
|
+
|
|
159
|
+
def __init__(self, fname, **kwargs): # pylint: disable=unused-argument
|
|
160
|
+
"""Initialise dumper.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
fname (str): output file name
|
|
164
|
+
**kwargs: addition keyword arguments for initialisation
|
|
165
|
+
|
|
166
|
+
"""
|
|
167
|
+
self._file = fname # dump file name
|
|
168
|
+
self._dump_header(**kwargs) # initialise output file
|
|
169
|
+
|
|
170
|
+
def __call__(self, value, name=None):
|
|
171
|
+
"""Dumper a new block.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
value (Dict[str, Any]): content to be dumped
|
|
175
|
+
name (v): name of current content block
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
Dumper: the dumper class itself (to support chain calling)
|
|
179
|
+
|
|
180
|
+
"""
|
|
181
|
+
with open(self._file, 'r+') as file:
|
|
182
|
+
self._append_value(value, file, name)
|
|
183
|
+
self._sptr = file.tell()
|
|
184
|
+
file.write(self._hend)
|
|
185
|
+
return self
|
|
186
|
+
|
|
187
|
+
##########################################################################
|
|
188
|
+
# Utilities.
|
|
189
|
+
##########################################################################
|
|
190
|
+
|
|
191
|
+
def _dump_header(self, **kwargs): # pylint: disable=unused-argument
|
|
192
|
+
"""Initially dump file heads and tails.
|
|
193
|
+
|
|
194
|
+
Keyword Args:
|
|
195
|
+
**kwargs: Arbitrary keyword arguments.
|
|
196
|
+
|
|
197
|
+
"""
|
|
198
|
+
with open(self._file, 'w') as file:
|
|
199
|
+
file.write(self._hsrt)
|
|
200
|
+
self._sptr = file.tell()
|
|
201
|
+
file.write(self._hend)
|
|
202
|
+
|
|
203
|
+
def _encode_func(self, o):
|
|
204
|
+
"""Check content type for function call.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
o (Any): object to check
|
|
208
|
+
|
|
209
|
+
See Also:
|
|
210
|
+
If the type of ``o`` is not defined in :attr:`~Dumper.__type__`,
|
|
211
|
+
the function refers to :meth:`~Dumper.default` for custom hooks.
|
|
212
|
+
|
|
213
|
+
"""
|
|
214
|
+
name = None
|
|
215
|
+
for (kind, code) in self.__type__:
|
|
216
|
+
if isinstance(o, kind):
|
|
217
|
+
name = code
|
|
218
|
+
break
|
|
219
|
+
if name is None:
|
|
220
|
+
name = self.default(o) # pylint: disable=assignment-from-no-return
|
|
221
|
+
|
|
222
|
+
func = '_append_%s' % name
|
|
223
|
+
return getattr(self, func)
|
|
224
|
+
|
|
225
|
+
def _encode_value(self, o):
|
|
226
|
+
"""Convert content for function call.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
o (Any): object to convert
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
Any: the converted object
|
|
233
|
+
|
|
234
|
+
See Also:
|
|
235
|
+
The function is a direct wrapper for :meth:`~Dumper.object_hook`.
|
|
236
|
+
|
|
237
|
+
"""
|
|
238
|
+
return self.object_hook(o)
|
|
239
|
+
|
|
240
|
+
@abc.abstractmethod
|
|
241
|
+
def _append_value(self, value, file, name):
|
|
242
|
+
"""Call this function to write contents.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
value (Dict[str, Any]): content to be dumped
|
|
246
|
+
file (io.TextIOWrapper): output file
|
|
247
|
+
name (str): name of current content block
|
|
248
|
+
|
|
249
|
+
"""
|