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 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'
@@ -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
@@ -0,0 +1,11 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Binary data processing utilities."""
3
+
4
+ import binascii
5
+
6
+
7
+ def hexlify(s):
8
+ """Hexadecimal representation of binary data."""
9
+ if hasattr(s, 'hex'):
10
+ return s.hex()
11
+ return binascii.hexlify(s).decode()
dictdumper/_types.py ADDED
@@ -0,0 +1,11 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Types compatibility."""
3
+
4
+ import sys
5
+
6
+ if sys.version_info.major < 3:
7
+ bytes_type = str
8
+ str_type = unicode
9
+ else:
10
+ bytes_type = bytes
11
+ str_type = str
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
+ """