Skip to content

Commit f04f6e2

Browse files
committed
Applied minor improvements
1 parent 697bcd6 commit f04f6e2

4 files changed

Lines changed: 65 additions & 27 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ This library extends the native [`codecs`](https://docs.python.org/3/library/cod
1616
$ pip install codext
1717
```
1818

19+
Want to contribute a new codec ? | Want to contribute a new macro ?
20+
:----------------------------------:|:---------------------------------
21+
Check the [documentation](https://python-codext.readthedocs.io/en/latest/howto.html) first<br>Then [PR](https://github.com/dhondta/python-codext/pulls) your new codec | [PR](https://github.com/dhondta/python-codext/pulls) your updated version of [`macros.json`](https://github.com/dhondta/python-codext/blob/master/codext/macros.json)
22+
1923
## :mag: Demonstrations
2024

2125
<p align="center"><img src="https://raw.githubusercontent.com/dhondta/python-codext/master/docs/demos/using-codext.gif" alt="Using CodExt from the command line"></p>

codext/__common__.py

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@
3333
__all__ = ["add", "add_macro", "add_map", "b", "clear", "codecs", "decode", "encode", "ensure_str", "examples", "guess",
3434
"isb", "generate_strings_from_regex", "get_alphabet_from_mask", "handle_error", "is_native",
3535
"list_categories", "list_encodings", "list_macros", "lookup", "maketrans", "os", "rank", "re", "register",
36-
"remove", "remove_macro", "reset", "s2i", "search", "stopfunc", "BytesIO", "MASKS", "PY3", "_input",
37-
"_stripl", "CodecMacro"]
36+
"remove", "reset", "s2i", "search", "stopfunc", "BytesIO", "MASKS", "PY3", "_input", "_stripl", "CodecMacro"]
3837
CODECS_REGISTRY = None
3938
CODECS_CATEGORIES = ["native", "custom"]
4039
MASKS = {
@@ -83,7 +82,7 @@ def __new__(cls, name):
8382
self.codecs = [lookup(e, False) for e in self.codecs] # lookup(e, False)
8483
self.parameters = {'name': name, 'category': "macro"} # ^ means that macros won't be nestable
8584
# test examples to check that the chain of encodings works
86-
for action, examples in (self.codecs[0].parameters.get('examples', {}) or {}).items():
85+
for action, examples in (self.codecs[0].parameters.get('examples', {}) or {'enc-dec(': ["T3st str!"]}).items():
8786
if re.match(r"enc(-dec)?\(", action):
8887
for e in (examples.keys() if action.startswith("enc(") else examples or []):
8988
rd = re.match(r"\@random(?:\{(\d+(?:,(\d+))*?)\})?$", e)
@@ -298,17 +297,23 @@ def add_macro(mname, *encodings):
298297
:param mname: macro name
299298
:param encodings: encoding names of the encodings to be chained with the macro
300299
"""
300+
global PERS_MACROS
301301
# check for name clash with alreday existing macros and codecs
302302
if mname in MACROS or mname in PERS_MACROS:
303303
raise ValueError("Macro name already exists")
304304
try:
305305
ci = lookup(mname, False)
306306
raise ValueError("Macro name clashes with codec '%s'" % ci.name)
307307
except LookupError:
308-
#TODO: test if the encodings sequence can work, using an example from the first codec
308+
pass
309+
try:
309310
PERS_MACROS[mname] = encodings
311+
CodecMacro(mname)
310312
with open(PERS_MACROS_FILE, 'w') as f:
311-
json.dump(PERS_MACROS, f)
313+
json.dump(PERS_MACROS, f, indent=2)
314+
except ValueError:
315+
del PERS_MACROS[mname]
316+
raise
312317
codecs.add_macro = add_macro
313318

314319

@@ -551,8 +556,8 @@ def __get_value(token, position, case_changed=False):
551556

552557
def clear():
553558
""" Clear codext's local registry of search functions. """
554-
global __codecs_registry
555-
__codecs_registry = []
559+
global __codecs_registry, MACROS, PERS_MACROS
560+
__codecs_registry, MACROS, PERS_MACROS = [], {}, {}
556561
codecs.clear = clear
557562

558563

@@ -648,34 +653,32 @@ def list_macros():
648653
return sorted(list(set(list(MACROS.keys()) + list(PERS_MACROS.keys()))))
649654

650655

651-
def remove(encoding):
652-
""" Remove all search functions matching the input encoding name from codext's local registry. """
656+
def remove(name):
657+
""" Remove all search functions matching the input encoding name from codext's local registry or any macro with the
658+
given name. """
659+
global __codecs_registry, MACROS, PERS_MACROS
653660
tbr = []
654661
for search_function in __codecs_registry:
655-
if search_function(encoding) is not None:
662+
if search_function(name) is not None:
656663
tbr.append(search_function)
657664
for search_function in tbr:
658665
__codecs_registry.remove(search_function)
659-
codecs.remove = remove
660-
661-
662-
def remove_macro(name):
663-
""" Remove the given macro from the macro registries. """
664666
try:
665667
del MACROS[name]
666668
except KeyError:
667669
pass
668670
try:
669671
del PERS_MACROS[name]
670672
with open(PERS_MACROS_FILE, 'w') as f:
671-
json.dump(PERS_MACROS, f)
673+
json.dump(PERS_MACROS, f, indent=2)
672674
except KeyError:
673675
pass
676+
codecs.remove = remove
674677

675678

676679
def reset():
677680
""" Reset codext's local registry of search functions and macros. """
678-
global CODECS_REGISTRY, MACROS, PERS_MACROS, __codecs_registry
681+
global __codecs_registry, CODECS_REGISTRY, MACROS, PERS_MACROS
679682
clear()
680683
d = os.path.dirname(__file__)
681684
for pkg in sorted(os.listdir(d)):

docs/features.md

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,31 @@ In this second example, we can see that:
161161

162162
-----
163163

164+
## Add a macro
165+
166+
**Macros** are chains of encodings. It is possible to define own macros with this feature. It works by giving the precedence to user's macros saved in `~/.codext-macros.json` then using embedded macros from the `codext` package.
167+
168+
Here is an example of adding a macro (and verifying it was indeed added):
169+
170+
```python
171+
>>> codext.list_macros()
172+
['example-macro']
173+
>>> codext.add_macro("test-macro", "gzip", "base64")
174+
>>> codext.list_macros()
175+
['example-macro', 'test-macro']
176+
```
177+
178+
!!! note "Removing a macro"
179+
180+
As macros are resolved like codecs (with the precedence for codecs), they can be removed the same way as a codec.
181+
182+
:::python
183+
>>> codext.remove("test-macro")
184+
185+
If this is a built-in macro, it will removed from the runtime list within the `codext` package. Next time this will be loaded, it will reset the builtin list of macros. Otherwise, if this is a custom macro, it will removed from the list of custom macros AND removed from `~/.codext-macros.json`.
186+
187+
-----
188+
164189
## List codecs
165190

166191
Codecs can be listed with the `list` function, either the whole codecs or only some categories.
@@ -225,7 +250,7 @@ Also, `codext` provides an `examples` function to get some examples of valid enc
225250

226251
-----
227252

228-
## Remove a custom encoding
253+
## Remove a custom encoding or macro
229254

230255
New codecs can be removed easily using the new function `remove`, which will only remove every codec matching the given encoding name in the proxy codecs registry and NOT in the native one.
231256

@@ -240,19 +265,25 @@ Traceback (most recent call last):
240265
LookupError: unknown encoding: bin
241266
```
242267

243-
While trying to remove a codec that is in the native registry won't raise a `LookupError`.
268+
Trying to remove a codec that is in the native registry won't raise a `LookupError`.
244269

245270
```python
246271
>>> codext.remove("utf-8")
247272
>>> codext.encode("test", "utf-8")
248273
b'test'
249274
```
250275

276+
Removing a macro works exactly the same way as for a codec.
277+
278+
```python
279+
>>> codext.remove("test-macro")
280+
```
281+
251282
-----
252283

253-
## Remove or restore `codext` encodings
284+
## Remove or restore `codext` encodings and macros
254285

255-
It can be useful while playing with encodings e.g. from Idle to be able to remove or restore `codext`'s encodings. This can be achieved using respectively the new `clear` and `reset` functions.
286+
It can be useful while playing with encodings and/or macros e.g. from Idle to be able to remove or restore `codext`'s encodings and macros. This can be achieved using respectively the new `clear` and `reset` functions.
256287

257288
```python
258289
>>> codext.clear()

tests/test_common.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ def test_handle_macros(self):
214214
MACRO = "test-macro-f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"
215215
STR = "this is a test"
216216
ENC = "H4sIAMrbkmEC/0txzyhIrnQC4QxPj6CcZONAWwAMIDOIFAAAAA=="
217-
codext.remove_macro(MACRO)
217+
codext.remove(MACRO)
218218
l = codext.list_macros()
219219
self.assertTrue(len(l) > 0)
220220
cm = codext.lookup("example-macro")
@@ -235,14 +235,14 @@ def test_handle_macros(self):
235235
json.dump(PERS_MACROS, f)
236236
codext.reset()
237237
self.assertRaises(ValueError, codext.lookup, MACRO)
238-
self.assertIsNone(codext.remove_macro(MACRO))
238+
self.assertIsNone(codext.remove(MACRO))
239239
self.assertRaises(LookupError, codext.lookup, MACRO)
240240
self.assertNotIn(MACRO, codext.list_macros())
241-
self.assertIsNone(codext.remove_macro("THIS-MACRO-DOES-NOT-EXIST"))
242-
self.assertIsNone(codext.remove_macro("VALID-MACRO"))
241+
self.assertIsNone(codext.remove("THIS-MACRO-DOES-NOT-EXIST"))
242+
self.assertIsNone(codext.remove("VALID-MACRO"))
243243
self.assertIsNone(codext.add_macro("VALID-MACRO", "gzip", "base64"))
244-
self.assertIsNone(codext.remove_macro("VALID-MACRO"))
244+
self.assertIsNone(codext.remove("VALID-MACRO"))
245245
self.assertIsNone(codext.add_macro("VALID-MACRO", "lzma", "base64"))
246-
self.assertIsNone(codext.remove_macro("VALID-MACRO"))
246+
self.assertIsNone(codext.remove("VALID-MACRO"))
247247
self.assertRaises(ValueError, codext.add_macro, "SHALL-FAIL", "base26", "sms", "letter-indices")
248248

0 commit comments

Comments
 (0)