├── txtcr ├── util │ ├── __init__.py │ ├── _utile.py │ └── inheritance_classes.py ├── requests │ ├── __init__.py │ └── requete.py ├── core │ ├── __init__.py │ ├── file_manager.py │ ├── types.py │ ├── encode.py │ └── decode.py └── __init__.py ├── tests ├── fichier.tcr ├── tests_encode.py └── tests_basic.py ├── .travis.yml ├── README.md ├── exemples ├── exemples.py ├── ex_serveur.py └── ex_client.py ├── .gitignore └── setup.py /txtcr/util/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /txtcr/requests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /txtcr/core/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | __version__ = '2.3.1' -------------------------------------------------------------------------------- /tests/fichier.tcr: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.7" 4 | os: linux 5 | script: 6 | - pip3.7 install . 7 | - python -m unittest discover -v -s tests -------------------------------------------------------------------------------- /txtcr/__init__.py: -------------------------------------------------------------------------------- 1 | # Decoding 2 | from txtcr.core.decode import decode 3 | from txtcr.core.encode import encode 4 | 5 | # Types and file manager 6 | from txtcr.core.types import * 7 | from txtcr.core.file_manager import FileManager 8 | 9 | # Classes built for inheritance 10 | from txtcr.util.inheritance_classes import Param 11 | 12 | # Requests 13 | from .requests import requete 14 | 15 | from txtcr.core import __version__ 16 | 17 | 18 | # File 19 | file = fichier = FileManager 20 | -------------------------------------------------------------------------------- /txtcr/util/_utile.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Python 3.6.2 3 | # ---------------------------------------------------------------------------- 4 | 5 | py_str = str 6 | py_bool = bool 7 | 8 | 9 | balises_categories = ['N', 'D', 'S', 'R', 'C', 'T', 'L', 'I'] 10 | 11 | balises_ouvrantes = ['<', '{', '[', '(', '"', "'", '+', '-', '/'] 12 | 13 | balises_fermentes = ['>', '}', ']', ')'] 14 | 15 | balises_separations = ['#', '=', ',', ':'] 16 | 17 | # Evite les problémes de syntaxe l'ors du passage à la version 3 18 | futur_balises_v3 = [':', '@'] 19 | 20 | chiffres = list('0123456789') 21 | 22 | balises = ( 23 | balises_ouvrantes 24 | + balises_fermentes 25 | + balises_separations 26 | + futur_balises_v3 27 | ) 28 | 29 | 30 | def is_class(obj): 31 | if '__dict__' in dir(obj.__class__) and '__dict__' in obj.__class__.__dict__: 32 | return True 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TXTCR (TCR) 2 | 3 | [![Build Status](https://travis-ci.com/4surix/txtcr.svg?branch=master)](https://travis-ci.com/4surix/txtcr) 4 | 5 | Format de données simple et efficace, créé pour l'envoie par sockets et la sauvegarde sur disques. 6 | 7 | Tout est expliqué dans le [wiki](https://github.com/4surix/txtcr/wiki) (encore en construction) ! Bonne lecture ! ✨ 8 | 9 | # Aperçu 10 | 11 | ## Non indenté 12 | 13 | ``` 14 | 15 | ``` 16 | 17 | ## Indenté 18 | 19 | ``` 20 | < 21 | N#Profil 22 | I#{ 23 | uid 12345678 24 | nom "Petite patate" 25 | langues [ 26 | français 27 | español 28 | ] 29 | réseaux [ 30 | ( 31 | discord 32 | "Patatie#1234" 33 | ) 34 | ( 35 | email 36 | patate@puree.com 37 | ) 38 | ] 39 | "activités favorites" [ 40 | manger 41 | dormir 42 | ] 43 | connectée True 44 | description None 45 | } 46 | > 47 | ``` -------------------------------------------------------------------------------- /tests/tests_encode.py: -------------------------------------------------------------------------------- 1 | 2 | import unittest 3 | 4 | 5 | import txtcr as tcr 6 | import txtcr.core.types as types 7 | 8 | 9 | class EncodingTests(unittest.TestCase): 10 | 11 | def test_str(self): 12 | test = "This is a string" 13 | self.assertEqual(f'"{test}"', tcr.encode(test)) 14 | 15 | def test_bytes(self): 16 | test = b'test' 17 | self.assertEqual(f"'test'", tcr.encode(test)) 18 | 19 | def test_bools(self): 20 | self.assertEqual('False', tcr.encode(False), msg="Builtin bool False") 21 | self.assertEqual('True', tcr.encode(True), msg="Builtin bool True") 22 | 23 | def test_nones(self): 24 | self.assertEqual('None', tcr.encode(None), msg="Builtin None") 25 | 26 | def test_int_float(self): 27 | self.assertEqual("4", tcr.encode(4), msg="Int") 28 | self.assertEqual("3.14", tcr.encode(3.14), msg="Float") 29 | 30 | def test_dict(self): 31 | test = {1: "test", 2: {"test": True}} 32 | self.assertEqual('{1 test 2 {test True}}', tcr.encode(test)) 33 | 34 | def test_list(self): 35 | self.assertEqual('[0 1 2]', tcr.encode([0, 1, 2])) 36 | 37 | def test_tuple(self): 38 | self.assertEqual('(3 4 5)', tcr.encode((3, 4, 5))) 39 | 40 | 41 | if __name__ == '__main__': 42 | unittest.main() 43 | -------------------------------------------------------------------------------- /txtcr/util/inheritance_classes.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Python 3.6.2 3 | # ---------------------------------------------------------------------------- 4 | 5 | 6 | class Param: 7 | """ 8 | Allows to create a class than inherits the given TCR parameter 9 | Permet de créer une classe qui va hériter d'un paramètre TCR. 10 | 11 | Params: 12 | - S / STR => class reaction to str() call 13 | - R / REPR => class reaction to repr() call 14 | - T / DATE => date the TCR was created at, or anything relevant. 15 | This parameter is always user-stated. 16 | 17 | Exemple: 18 | class Test(txtcr.Param.REPR["<{I#.pouf}>"]): 19 | # cette classe va hériter 20 | # de la capacité à afficher la valeur de pouf (rouge) 21 | # lors d'un repr() de la classe 22 | # this class will inherit capacity to print pouf value (rouge) 23 | # when you call repr() with this class or inst 24 | pouf = rouge 25 | """ 26 | 27 | class PrebuiltRepr: 28 | 29 | def __getitem__(self, item): 30 | class R: 31 | repr__ = item 32 | 33 | return R 34 | 35 | R = REPR = PrebuiltRepr() 36 | 37 | class PrebuiltStr: 38 | 39 | def __getitem__(self, item): 40 | class S: 41 | str__ = item 42 | 43 | return S 44 | 45 | S = STR = PrebuiltStr() 46 | 47 | class PrebuiltTime: 48 | 49 | def __getitem__(self, item): 50 | class T: 51 | date__ = item 52 | 53 | return T 54 | 55 | T = DATE = PrebuiltTime() 56 | 57 | -------------------------------------------------------------------------------- /exemples/exemples.py: -------------------------------------------------------------------------------- 1 | 2 | import txtcr 3 | 4 | 5 | print("-" * 30) 6 | 7 | class Pouf(txtcr.Param.S["{N#}"]): 8 | pomme = "rouge" 9 | nombre = 10 10 | fraise = ["rouge", "blanche"] 11 | 12 | class Pouet(txtcr.Param.S["<{I#.pouf}>"]): 13 | pouf = Pouf 14 | patapouf = True 15 | 16 | # Avec et sans indent 17 | r = txtcr.encode(Pouet) 18 | print(r) 19 | r = txtcr.encode(Pouet, indent=4) 20 | print(r) 21 | 22 | print("-" * 30) 23 | 24 | r = txtcr.decode(r) 25 | print(r) 26 | 27 | print("-" * 30) 28 | 29 | class Exemple: 30 | uid = 1234 31 | nom = "pouet" 32 | conn = True 33 | absent = False 34 | img = None 35 | 36 | tcr = txtcr.encode(Exemple) 37 | 38 | print(tcr) 39 | 40 | print("-" * 30) 41 | 42 | # Tester si l'encodage/decodage simmultané fonctionne 43 | for _ in range(3): 44 | tcr = txtcr.decode(tcr) 45 | 46 | print(tcr) 47 | 48 | tcr = txtcr.encode(tcr, indent=4) 49 | 50 | print(tcr) 51 | 52 | print("-" * 30) 53 | 54 | # L'ouverture de fichier 55 | with txtcr.fichier('fichier.tcr', 'w', indent=4) as tcr: 56 | tcr.pomme = "rouge" 57 | tcr.poire = "jaune" 58 | #Enregistrement automatique 59 | 60 | print(tcr) 61 | 62 | print("-" * 30) 63 | 64 | # Test ancienne et nouvelle syntaxe 65 | 66 | tcr = """ 67 | // AncienneSyntaxe // 68 | 69 | {"None" O"bla bla" 70 | "False" 0"bla bla" 71 | "True" 1"bla bla" 72 | } 73 | """ 74 | 75 | print(txtcr.decode(tcr)) 76 | 77 | tcr = """ 78 | // NouvelleSyntaxe // 79 | 80 | {"None" None 81 | "False" False 82 | "True" True 83 | } 84 | """ 85 | 86 | print(txtcr.decode(tcr)) 87 | 88 | print("-" * 30) 89 | 90 | input('Pressez entrée pour quitter.') 91 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Perso 2 | discord/ 3 | bot.py 4 | 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | 63 | # Flask stuff: 64 | instance/ 65 | .webassets-cache 66 | 67 | # Scrapy stuff: 68 | .scrapy 69 | 70 | # Sphinx documentation 71 | docs/_build/ 72 | 73 | # PyBuilder 74 | target/ 75 | 76 | # Jupyter Notebook 77 | .ipynb_checkpoints 78 | 79 | # pyenv 80 | .python-version 81 | 82 | # celery beat schedule file 83 | celerybeat-schedule 84 | 85 | # SageMath parsed files 86 | *.sage.py 87 | 88 | # Environments 89 | .env 90 | .venv 91 | env/ 92 | venv/ 93 | ENV/ 94 | env.bak/ 95 | venv.bak/ 96 | 97 | # Spyder project settings 98 | .spyderproject 99 | .spyproject 100 | 101 | # Rope project settings 102 | .ropeproject 103 | 104 | # mkdocs documentation 105 | /site 106 | 107 | # mypy 108 | .mypy_cache/ 109 | 110 | # Perso 111 | .idea/ 112 | DebugUtils/ 113 | txtcr/version.py 114 | bin/log.txt -------------------------------------------------------------------------------- /exemples/ex_serveur.py: -------------------------------------------------------------------------------- 1 | import txtcr 2 | import websockets 3 | 4 | from txtcr.requests.requete import * # Requetes, POST, GET, ... 5 | 6 | 7 | class INFO: 8 | phrase = None 9 | users = {} 10 | 11 | 12 | ### Création des requetes 13 | 14 | reqserv = txtcr.requests.requete.Requetes() 15 | 16 | 17 | # Création d'une requête CONNEXION 18 | # qui répondra à la commande POST 19 | @reqserv.add(name='CONNEXION', cmd='POST') 20 | def add_client(requete, client, *, id:int): 21 | 22 | # Ajout du client 23 | INFO.users[id] = client 24 | 25 | class CONNEXION(RESPONSE[200]): 26 | status = 'Connectée.' 27 | 28 | # Renvoie la requete CONNEXION au client 29 | return CONNEXION 30 | 31 | 32 | # Création d'une requête PHRASE 33 | # qui répondra à la commande GET 34 | @reqserv.add(name='PHRASE', cmd='GET') 35 | def client_recup_phrase(requete, client): 36 | 37 | class PHRASE(RESPONSE[200]): 38 | phrase = INFO.phrase 39 | 40 | # Renvoie la requete PHRASE au client 41 | return PHRASE 42 | 43 | 44 | @reqserv.add(name='PHRASE', cmd='POST') 45 | async def modif_phrase(requete, client, *, phrase:str=None): 46 | 47 | print(INFO.phrase, '->', phrase) 48 | 49 | INFO.phrase = phrase 50 | 51 | class PHRASE(RESPONSE[200]): 52 | phrase = INFO.phrase 53 | 54 | reponse = txtcr.encode(PHRASE) 55 | 56 | for user in INFO.users.values(): 57 | await user.send(reponse) 58 | 59 | 60 | @reqserv.add('_EXCEPTION') 61 | def _EXCEPTION(requete, client, *args, **kwargs): 62 | 63 | print(args, kwargs) 64 | 65 | # Ne retourne rien 66 | 67 | 68 | ### Serveur 69 | 70 | async def reception_client(websocket, path): 71 | 72 | try: 73 | while True: 74 | reçu = await websocket.recv() 75 | reponse = await reqserv.recv(reçu, websocket) 76 | if reponse: 77 | await websocket.send(reponse) 78 | 79 | except websockets.exceptions.ConnectionClosed: 80 | # Si un client se déconecte du serveur 81 | for cid, client in INFO.users.items(): 82 | if client == websocket: 83 | del INFO.users[cid] 84 | break 85 | 86 | 87 | async def run(): 88 | print('Serveur prêt.') 89 | 90 | await websockets.serve(reception_client, '127.0.0.1', 6000) 91 | 92 | # --------------- 93 | 94 | loop = asyncio.get_event_loop() 95 | loop.run_until_complete(run()) 96 | loop.run_forever() 97 | 98 | # ---------------- -------------------------------------------------------------------------------- /exemples/ex_client.py: -------------------------------------------------------------------------------- 1 | """ 2 | Exemple de client utilisant websocket et txtcr. 3 | Ouvrez plusieurs client pour tester ! 4 | """ 5 | 6 | import txtcr 7 | 8 | import random 9 | import websockets 10 | 11 | from txtcr.requests.requete import * # Requetes, POST, GET, ... 12 | from aioconsole import ainput 13 | 14 | 15 | ### Mise en place des requêtes 16 | 17 | reqclt = txtcr.requests.requete.Requetes() 18 | 19 | 20 | @reqclt.add(cmd=200) 21 | def CONNEXION(requete, status): 22 | 23 | print('STATUS:', status) 24 | 25 | # Ne renvoie rien au serveur 26 | 27 | 28 | @reqclt.add(cmd=200) 29 | def PHRASE(requete, phrase): 30 | 31 | print('PHRASE:', phrase) 32 | 33 | # Ne renvoie rien au serveur 34 | 35 | 36 | ### Mise en place du client 37 | 38 | class Client: 39 | 40 | def __init__(self): 41 | 42 | self.url = "ws://127.0.0.1:6000" 43 | 44 | self.ws = True 45 | 46 | async def start(self): 47 | 48 | class CONNEXION(POST): 49 | id = random.randint(0, 100000) 50 | 51 | class TEST(POST): 52 | pass 53 | réponse = txtcr.encode(CONNEXION) + txtcr.encode(TEST) 54 | 55 | while True: 56 | 57 | if réponse: 58 | await self.ws.send(réponse) 59 | 60 | réponse = await reqclt.recv(await self.ws.recv()) 61 | 62 | async def run(self): 63 | 64 | self.ws = await websockets.connect(self.url) 65 | 66 | try: 67 | await self.start() 68 | 69 | except websockets.exceptions.ConnectionClosed: 70 | # Si le serveur se déconnecte 71 | self.ws = None 72 | 73 | client = Client() 74 | 75 | 76 | async def run_prog(): 77 | 78 | print("Ecrivez n'importe quel texte, cela l'envera au serveur." 79 | + "\nEcrivez 'GET' pour récupérer le dernier texte envoyé.") 80 | 81 | while client.ws: 82 | 83 | texte = await ainput() 84 | 85 | if not client.ws: 86 | break 87 | 88 | if texte == 'GET': 89 | 90 | class PHRASE(GET): 91 | pass 92 | 93 | else: 94 | 95 | class PHRASE(POST): 96 | phrase = texte 97 | 98 | await client.ws.send(txtcr.encode(PHRASE)) 99 | 100 | 101 | # ----------------------- 102 | 103 | loop = asyncio.get_event_loop() 104 | asyncio.ensure_future(client.run()) 105 | asyncio.ensure_future(run_prog()) 106 | loop.run_forever() 107 | 108 | # ------------------- -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import subprocess 4 | 5 | DOCLINES = (__doc__ or '').split("\n") 6 | 7 | if sys.version_info[:2] < (3, 6): 8 | raise RuntimeError("Python version >= 3.6 required.") 9 | 10 | 11 | MAJOR = 1 12 | MINOR = 0 13 | MICRO = 0 14 | IS_RELEASED = False 15 | VERSION = '%d.%d.%d' % (MAJOR, MINOR, MICRO) 16 | 17 | 18 | def git_version(): 19 | try: 20 | rev = subprocess.check_output(['git', 'rev-parse', 'HEAD']) 21 | return rev.strip().decode('ascii') 22 | except (subprocess.SubprocessError, OSError): 23 | return "Unknown" 24 | 25 | 26 | def get_version_info(): 27 | full_version = VERSION 28 | if os.path.exists('.git'): 29 | git_revision = git_version() 30 | else: 31 | git_revision = "Unknown" 32 | 33 | if not IS_RELEASED: 34 | full_version += '.dev0+' + git_revision[:7] 35 | 36 | return full_version, git_revision 37 | 38 | 39 | def write_version_py(filename='txtcr/version.py'): 40 | full_version, git_revision = get_version_info() 41 | 42 | content = "# Version file generated by setup.py \n" \ 43 | "\n" \ 44 | f"short_version = '{VERSION}' \n" \ 45 | f"version = '{VERSION}' \n" \ 46 | f"full_version = '{full_version}' \n" \ 47 | f"git_revision = '{git_revision}' \n" \ 48 | f"release = {str(IS_RELEASED)} \n" \ 49 | "\n" \ 50 | "\n" \ 51 | f"if not release: \n" \ 52 | f" version = full_version \n" 53 | 54 | with open(filename, 'w') as f: 55 | f.write(content) 56 | 57 | 58 | def setup_package(): 59 | from setuptools import setup 60 | write_version_py() 61 | 62 | setup( 63 | name='TXTCR', 64 | version=get_version_info()[0], 65 | description='', 66 | url='https://github.com/4surix/txtcr', 67 | project_urls={ 68 | 'Source Code': 'https://github.com/4surix/txtcr', 69 | 'Bug Tracker': 'https://github.com/4surix/txtcr/issues', 70 | }, 71 | 72 | author='4surix', 73 | author_email='john.doe@example.com', 74 | maintainer='Fabien "BioTheWolff" Z.', 75 | maintainer_email='contact.biowolf@gmx.fr', 76 | 77 | packages=[ 78 | 'txtcr', 79 | 'txtcr.core', 80 | 'txtcr.util', 81 | 'txtcr.requests' 82 | ], 83 | 84 | python_requires='>=3.6', 85 | zip_safe=False 86 | ) 87 | 88 | 89 | if __name__ == '__main__': 90 | setup_package() 91 | -------------------------------------------------------------------------------- /txtcr/core/file_manager.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Python 3.6.2 3 | # ---------------------------------------------------------------------------- 4 | 5 | from .decode import decode 6 | from .encode import encode 7 | 8 | 9 | class FileManager: 10 | """ 11 | Class allowing to quickly edit, save and get TCR files 12 | Classe permettant de gérer des fichiers rapidement 13 | et des les écrire/lire en TCR 14 | 15 | Ex: 16 | with fichier('path/name.tcr') as f: 17 | f.pouet = "pouf" 18 | 19 | # Save Auto 20 | 21 | f = fichier('path/name.tcr') 22 | 23 | data = f() # Get data 24 | f(data) # Save data 25 | 26 | # or 27 | 28 | data = f.read() 29 | f.write(data) 30 | """ 31 | 32 | def __init__(self, chemin, mode='r', *, indent=0, default=None): 33 | 34 | self.chemin = chemin 35 | self.mode = mode 36 | 37 | self.indent = indent 38 | self.default = default 39 | 40 | def __enter__(self): 41 | 42 | self.data = self.__open() 43 | return self.data 44 | 45 | def __exit__(self, exception_type, exception_value, tracing): 46 | 47 | if self.mode == 'w': 48 | self.__save(self.data) 49 | 50 | def __open(self): 51 | 52 | try: 53 | fichier = open(self.chemin, 'r', encoding='utf-8') 54 | data = decode(fichier.read()) 55 | 56 | except FileNotFoundError as exception: 57 | # Dans le cas où le fichier n'existerai pas, 58 | # si une valeur par défaut a été definie 59 | # alors créer un ficher avec celle ci, 60 | # sinon léve l'erreur. 61 | 62 | if self.default: 63 | fichier = open(self.chemin, 'w', encoding='utf-8') 64 | data = decode(self.default) 65 | 66 | else: 67 | raise exception 68 | 69 | fichier.close() 70 | 71 | return data 72 | 73 | def __save(self, data): 74 | 75 | # Encode avant pour voir si tout est bon avant d'ouvrir le fichier. 76 | data = encode(data, indent=self.indent) 77 | # Enregistrement 78 | fichier = open(self.chemin, 'w', encoding='utf-8') 79 | fichier.write(data) 80 | fichier.close() 81 | 82 | def __call__(self, data=None): 83 | 84 | if not data: 85 | return self.__open() 86 | else: 87 | self.__save(data) 88 | 89 | def read(self): 90 | return self.__open() 91 | 92 | def write(self, data, *, indent=0): 93 | self.indent = indent if indent else self.indent 94 | 95 | self.__save(data) 96 | -------------------------------------------------------------------------------- /tests/tests_basic.py: -------------------------------------------------------------------------------- 1 | 2 | import unittest 3 | 4 | 5 | import txtcr 6 | import datetime 7 | 8 | 9 | class BasicTests(unittest.TestCase): 10 | """ 11 | The basic tests for the TCR module. 12 | More aimed tests can be found in other files, in order to add more coverage to the code 13 | """ 14 | 15 | # 16 | # CLASSES INHERITANCE: GLOBAL COMPARISON 17 | # 18 | def test_inherit_strClass_print_full_class(self): 19 | class TestClass(txtcr.Param.STR["<{I#.pouf}>"]): 20 | test = 10 21 | 22 | test = txtcr.encode(TestClass) 23 | self.assertEqual('" I#{test 10}>', test) 24 | 25 | def test_inherit_reprClass_print_full_class(self): 26 | class TestClass(txtcr.Param.REPR["<{I#.pouf}>"]): 27 | test = 10 28 | 29 | test = txtcr.encode(TestClass) 30 | self.assertEqual('" I#{test 10}>', test) 31 | 32 | def test_inheritance_dateClass_print_full_class(self): 33 | date = str(datetime.datetime.today()) 34 | 35 | class TestClass(txtcr.Param.DATE[date]): 36 | test = 10 37 | 38 | test = txtcr.encode(TestClass) 39 | self.assertEqual('', test) 40 | 41 | # 42 | # CLASSES INHERITANCE: STR SPECIALS 43 | # 44 | def test_inherit_strClass_print_name(self): 45 | class TestClass(txtcr.Param.STR["{N#}"]): 46 | pomme = "rouge" 47 | 48 | test = txtcr.decode(txtcr.encode(TestClass)) 49 | 50 | self.assertEqual("TestClass", str(test)) 51 | 52 | def test_inherit_strClass_print_content(self): 53 | class TestClass(txtcr.Param.STR["{I#.pomme}"]): 54 | pomme = "rouge" 55 | nombre = 10 56 | fraise = ["rouge", "blanche"] 57 | 58 | test = txtcr.decode(txtcr.encode(TestClass)) 59 | 60 | self.assertEqual("rouge", str(test)) 61 | 62 | # 63 | # CLASSES INHERITANCE: REPR SPECIALS 64 | # 65 | def test_inherit_reprClass_print_name(self): 66 | class TestClass(txtcr.Param.REPR["{N#}"]): 67 | pomme = "rouge" 68 | 69 | test = txtcr.decode(txtcr.encode(TestClass)) 70 | 71 | self.assertEqual("TestClass", repr(test)) 72 | 73 | def test_inherit_reprClass_print_content(self): 74 | class TestClass(txtcr.Param.REPR["{I#.pomme}"]): 75 | pomme = "rouge" 76 | nombre = 10 77 | fraise = ["rouge", "blanche"] 78 | 79 | test = txtcr.decode(txtcr.encode(TestClass)) 80 | 81 | self.assertEqual("rouge", repr(test)) 82 | 83 | # 84 | # EMBEDDED CLASS WITH INHERITANCE 85 | # 86 | def test_inherit_print_fully_embedded_class(self): 87 | class ToBeInherited(txtcr.Param.STR["{N#}"]): 88 | pomme = "rouge" 89 | nombre = 10 90 | fraise = ["rouge", "blanche"] 91 | 92 | class TestClass(txtcr.Param.STR["<{I#.pouf}>"]): 93 | pouf = ToBeInherited 94 | patapouf = True 95 | 96 | test = txtcr.encode(TestClass) 97 | 98 | self.assertEqual( 99 | '" I#{pouf patapouf True}>' 102 | , 103 | test 104 | ) 105 | 106 | # 107 | # FILE 108 | # 109 | def test_file(self): 110 | filepath = 'fichier.tcr' 111 | backuppath = 'tests/fichier.tcr' 112 | 113 | def test(path): 114 | # Concentrating on editing and "compiling" 115 | with txtcr.file(path, 'w') as tcr: 116 | tcr["N#"] = "File Test" 117 | tcr.pomme = "rouge" 118 | tcr.poire = "jaune" 119 | 120 | self.assertEqual( 121 | "<:TCR: File Test>", str(tcr), msg="File compiling in TCR" 122 | ) 123 | 124 | # Concentrating on what was saved in the file 125 | with txtcr.file(path, 'w') as tcr2: 126 | tcr2["N#"] = "File Test" 127 | tcr2.pomme = "mangée" 128 | tcr2.poire = "pourrie" 129 | 130 | with open(path, 'r', encoding='utf-8') as f: 131 | c = f.read() 132 | 133 | self.assertEqual( 134 | '', 135 | c, 136 | msg="File: comparing saved TCR to expected" 137 | ) 138 | 139 | try: 140 | test(filepath) 141 | except FileNotFoundError: 142 | test(backuppath) 143 | 144 | 145 | if __name__ == '__main__': 146 | unittest.main() 147 | -------------------------------------------------------------------------------- /txtcr/requests/requete.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Python 3.6.2 3 | # ---------------------------------------------------------------------------- 4 | 5 | import asyncio 6 | import traceback 7 | 8 | from txtcr.core.encode import encode 9 | from txtcr.core.decode import decode 10 | 11 | 12 | class Requetes: 13 | 14 | def __init__(self): 15 | self._requetes = {} 16 | 17 | # A utiliser comme décorateur pour créer des requêtes 18 | # @inst.add(200) 19 | def add(self, name=None, cmd=None): 20 | 21 | def _recup_func(function): 22 | 23 | name = _recup_func.name 24 | cmd = _recup_func.cmd 25 | 26 | if name is None: 27 | name = function.__name__ 28 | 29 | # Requêtes spéciales sans cmd 30 | if name in ['_OTHER', '_EXCEPTION', '_ALL']: 31 | self._requetes[name] = function 32 | 33 | else: 34 | if name not in self._requetes: 35 | self._requetes[name] = {} 36 | 37 | self._requetes[name][cmd] = function 38 | 39 | return function 40 | 41 | # Pour une raison que je ne connais pas il ne trouve pas les 42 | # variable name et cmd tout seul 43 | _recup_func.name = name 44 | _recup_func.cmd = cmd 45 | 46 | return _recup_func 47 | 48 | async def recv(self, msg, *args, **kwargs): 49 | 50 | requetes = decode(msg, ever_list=True) 51 | 52 | returns = [] 53 | 54 | def return_(result): 55 | if isinstance(result, (list, tuple)): 56 | for r in result: 57 | returns.append(r) 58 | 59 | elif result is not None: 60 | returns.append(result) 61 | 62 | for requete in requetes: 63 | 64 | nom = requete.get('N#') 65 | cmd = requete.get('C#') 66 | info = requete.get('I#', {}) 67 | 68 | func = self._requetes.get(nom, {}).get(cmd) 69 | 70 | if func: 71 | # Appel de la fonction correspondant à la commande 72 | return_( 73 | await self.__call_fonc( 74 | func, requete, 75 | *args, **info, **kwargs 76 | ) 77 | ) 78 | 79 | else: 80 | # Si la commande n'existe pas ou n'a pas été definie 81 | # génére la fonction correspondant à OTHER 82 | 83 | func_autre = self._requetes.get('_OTHER') 84 | 85 | if func_autre: 86 | return_( 87 | await self.__call_fonc( 88 | func_autre, requete, cmd, nom, 89 | *args, **info, **kwargs 90 | ) 91 | ) 92 | 93 | # Pour la fonction spécial appelée pour toute sorte de requête 94 | func_all = self._requetes.get('_ALL') 95 | 96 | if func_all: 97 | return_( 98 | await self.__call_fonc( 99 | func_all, requete, cmd, nom, 100 | *args, **info, **kwargs 101 | ) 102 | ) 103 | 104 | return ''.join(encode(r) for r in returns) if returns else None 105 | 106 | async def __call_fonc(self, fonction, requete, *args, **kwargs): 107 | 108 | async def call(): 109 | return ( 110 | await fonction(requete, *args, **kwargs) if ( 111 | asyncio.iscoroutine(fonction) 112 | or asyncio.iscoroutinefunction(fonction) 113 | ) 114 | else 115 | fonction(requete, *args, **kwargs) 116 | ) 117 | 118 | func_except = self._requetes.get('_EXCEPTION') 119 | 120 | if func_except: 121 | try: return await call() 122 | except Exception as e: 123 | exception = (e.__class__.__name__, str(e)) 124 | suivis = traceback.format_exc() 125 | return await self.__call_fonc( 126 | func_except, requete, exception, suivis, 127 | *args, **kwargs 128 | ) 129 | 130 | else: 131 | return await call() 132 | 133 | 134 | # Raccourcis 135 | class GET: 136 | cmdcode__ = 'GET' 137 | 138 | 139 | class POST: 140 | cmdcode__ = 'POST' 141 | 142 | 143 | class DELETE: 144 | cmdcode__ = 'DELETE' 145 | 146 | 147 | class PUT: 148 | cmdcode__ = 'PUT' 149 | 150 | 151 | class OPTIONS: 152 | cmdcode__ = 'OPTIONS' 153 | 154 | 155 | class RESPONSE: 156 | 157 | def __getitem__(self, item): 158 | class Response: 159 | cmdcode__ = item 160 | 161 | return Response 162 | 163 | 164 | RESPONSE = RESPONSE() 165 | -------------------------------------------------------------------------------- /txtcr/core/types.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Python 3.6.2 3 | # ---------------------------------------------------------------------------- 4 | 5 | from txtcr.util._utile import * 6 | 7 | 8 | # Nbr 9 | def mk_nbr(texte): 10 | if ',' in texte: 11 | nbr = float(texte.replace(',', '.')) 12 | elif '.' in texte: 13 | nbr = float(texte) 14 | else: 15 | nbr = int(texte) 16 | 17 | return nbr 18 | 19 | 20 | # Class 21 | def new_clss(encode): 22 | class Clss: 23 | 24 | str__ = None 25 | repr__ = None 26 | cmdcode__ = None 27 | date__ = None 28 | hash__ = None 29 | 30 | def __init__(self): 31 | 32 | Clss.__name__ = '' 33 | Clss.encode__ = encode 34 | 35 | def __len__(self): 36 | 37 | return len(self.__dict__) 38 | 39 | def __str__(self): 40 | 41 | istr = Clss.str__ 42 | 43 | if istr: 44 | return self.__format(istr) 45 | else: 46 | return '<:TCR: %s>' % Clss.__name__ 47 | 48 | def __repr__(self): 49 | 50 | irepr = Clss.repr__ 51 | 52 | if irepr: 53 | return self.__format(irepr) 54 | else: 55 | return str(self) 56 | 57 | def __format(self, texte): 58 | 59 | return texte.format(**{ 60 | 'N#': Clss.__name__, 61 | 'D#': Clss.__doc__, 62 | 'S#': Clss.str__, 63 | 'R#': Clss.repr__, 64 | 'C#': Clss.cmdcode__, 65 | 'T#': Clss.date__, 66 | 'H#': Clss.hash__, 67 | 'I#': self 68 | }) 69 | 70 | def __getitem__(self, item): 71 | 72 | return self.__dict__[item] 73 | 74 | def __setitem__(self, item, value): 75 | 76 | if item == "N#": 77 | Clss.__name__ = value 78 | 79 | elif item == "D#": 80 | Clss.__doc__ = value 81 | 82 | elif item == 'R#': 83 | Clss.repr__ = value 84 | 85 | elif item == 'S#': 86 | Clss.str__ = value 87 | 88 | elif item == "C#": 89 | Clss.cmdcode__ = value 90 | 91 | elif item == "T#": 92 | Clss.date__ = value 93 | 94 | elif item == "H#": 95 | Clss.hash__ = value 96 | 97 | elif item == "I#": 98 | for item, value in value.items(): 99 | self.__dict__[item] = value 100 | 101 | else: 102 | self.__dict__[item] = value 103 | 104 | def __delitem__(self, item): 105 | 106 | del self.__dict__[item] 107 | 108 | def __iter__(self): 109 | 110 | for item in self.__dict__: 111 | yield item 112 | 113 | def keys(self): 114 | 115 | return self.__dict__.keys() 116 | 117 | def values(self): 118 | 119 | return self.__dict__.items() 120 | 121 | def items(self): 122 | 123 | return self.__dict__.items() 124 | 125 | def encode(self, indent = 0): 126 | 127 | return Clss.encode__(self, indent = indent) 128 | 129 | def get(self, item, defaut=None): 130 | 131 | value = { 132 | 'N#': '__name__', 133 | 'D#': '__doc__', 134 | 'R#': 'repr__', 135 | 'S#': 'str__', 136 | 'C#': 'cmdcode__', 137 | 'T#': 'date__', 138 | 'H#': 'hash__' 139 | }.get(item) 140 | 141 | if value: 142 | return getattr(Clss, value, defaut) 143 | 144 | elif item == 'I#': 145 | return ( 146 | {**self.__dict__} if self.__dict__ 147 | else 148 | defaut 149 | ) 150 | 151 | else: 152 | return self.__dict__.get(item, defaut) 153 | 154 | def setdefault(self, item, defaut=None): 155 | 156 | value = { 157 | 'N#': '__name__', 158 | 'D#': '__doc__', 159 | 'R#': 'repr__', 160 | 'S#': 'str__', 161 | 'C#': 'cmdcode__', 162 | 'T#': 'date__', 163 | 'H#': 'hash__' 164 | }.get(item) 165 | 166 | if value: 167 | value = getattr(Clss, value, None) 168 | 169 | if value is None: 170 | setattr(Clss, value, defaut) 171 | value = defaut 172 | 173 | return value 174 | 175 | elif item == 'I#': 176 | if not self.__dict__: 177 | for k, v in defaut.items(): 178 | self.__dict__[k] = v 179 | 180 | return {**self.__dict__} 181 | 182 | else: 183 | return self.__dict__.setdefault(item, defaut) 184 | 185 | 186 | return Clss() 187 | -------------------------------------------------------------------------------- /txtcr/core/encode.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Python 3.6.2 3 | # ---------------------------------------------------------------------------- 4 | 5 | from txtcr.core.types import * 6 | from txtcr.util._utile import balises 7 | 8 | 9 | def encode(data, *, profondeur = -1, position = [0], indent = 0): 10 | 11 | profondeur += 1 12 | 13 | # String 14 | if isinstance(data, str): 15 | 16 | str_simple = data and data[0] not in '0123456789' 17 | 18 | if str_simple: 19 | for carac in data: 20 | if ( 21 | carac == ' ' 22 | or carac == '\n' 23 | or carac == '\t' 24 | or carac == '\\' 25 | or carac in balises 26 | ): 27 | str_simple = False 28 | break 29 | 30 | if str_simple: 31 | value_encoded = data 32 | else: 33 | value_encoded = ( 34 | '"%s"' 35 | % data 36 | .replace('\\', '\\\\') 37 | .replace('"', '\\"') 38 | .replace('\n', '\\n') 39 | .replace('\t', '\\t') 40 | ) 41 | 42 | # Bytes 43 | elif isinstance(data, bytes): 44 | value_encoded = ( 45 | "'%s'" 46 | % data.decode() 47 | .replace('\\', '\\\\') 48 | .replace("'", "\\'") 49 | .replace('\n', '\\n') 50 | .replace('\t', '\\t') 51 | ) 52 | 53 | # Boolean 54 | elif isinstance(data, bool): 55 | if data: 56 | value_encoded = 'True' 57 | else: 58 | value_encoded = 'False' 59 | 60 | elif isinstance(data, (int, float)): 61 | value_encoded = "%s" % data 62 | 63 | elif isinstance(data, dict): 64 | 65 | value_encoded = '{' 66 | 67 | espace = '' 68 | 69 | espacement = ( 70 | '' if not indent 71 | else 72 | '\n' + ' ' * indent * (profondeur + 1) 73 | ) 74 | 75 | for key__, value__ in data.items(): 76 | 77 | key = encode( 78 | key__, 79 | profondeur = profondeur, 80 | indent = indent 81 | ) 82 | 83 | value = encode( 84 | value__, 85 | profondeur = profondeur, 86 | position = position + [key__], 87 | indent = indent 88 | ) 89 | 90 | value_encoded += f"{espacement}{espace}{key} {value}" 91 | 92 | if not indent: 93 | espace = ' ' 94 | 95 | if indent: 96 | value_encoded += '\n' + ' ' * indent * profondeur 97 | 98 | value_encoded += '}' 99 | 100 | elif isinstance(data, list): 101 | 102 | value_encoded = '[' 103 | 104 | espace = '' 105 | 106 | espacement = ( 107 | '' if not indent 108 | else 109 | '\n' + ' ' * indent * (profondeur + 1) 110 | ) 111 | 112 | for index, value in enumerate(data): 113 | 114 | value = encode( 115 | value, 116 | profondeur = profondeur, 117 | position = position + [index], 118 | indent = indent 119 | ) 120 | 121 | value_encoded += f"{espacement}{espace}{value}" 122 | 123 | if not indent: 124 | espace = ' ' 125 | 126 | if indent: 127 | value_encoded += '\n' + ' ' * indent * profondeur 128 | 129 | value_encoded += ']' 130 | 131 | elif isinstance(data, tuple): 132 | 133 | value_encoded = '(' 134 | 135 | espace = '' 136 | 137 | espacement = ( 138 | '' if not indent 139 | else 140 | '\n' + ' ' * indent * (profondeur + 1) 141 | ) 142 | 143 | for index, value in enumerate(data): 144 | 145 | value = encode( 146 | value, 147 | profondeur = profondeur, 148 | position = position + [index], 149 | indent = indent 150 | ) 151 | 152 | value_encoded += f"{espacement}{espace}{value}" 153 | 154 | if not indent: 155 | espace = ' ' 156 | 157 | if indent: 158 | value_encoded += '\n' + ' ' * indent * profondeur 159 | 160 | value_encoded += ')' 161 | 162 | elif data is None: 163 | value_encoded = 'None' 164 | 165 | elif is_class(data): 166 | 167 | # Instance ou Class 168 | 169 | clss = data.__class__ 170 | clss_info = clss.__dict__ 171 | 172 | if '__weakrefoffset__' in clss_info: 173 | clss = data 174 | clss_info = clss.__dict__ 175 | 176 | value_encoded = '<' 177 | 178 | espace = '' 179 | 180 | espacement = ( 181 | '' if not indent 182 | else 183 | '\n' + ' ' * indent * (profondeur + 1) 184 | ) 185 | 186 | for balise, value in zip( 187 | [ 188 | 'N#', 189 | 'D#', 190 | 'R#', 191 | 'S#', 192 | 'C#', 193 | 'T#', 194 | 'H#', 195 | 'I#' 196 | ], 197 | [ 198 | clss.__name__ or None, 199 | clss.__doc__ or None, 200 | getattr(data, 'repr__', None), 201 | getattr(data, 'str__', None), 202 | getattr(data, 'cmdcode__', None), 203 | getattr(data, 'date__', None), 204 | getattr(data, 'hash__', None), 205 | { 206 | k: v 207 | for k, v in data.__dict__.items() 208 | if str(k)[-2:] != '__' 209 | } or None 210 | ] 211 | ): 212 | 213 | if value is None: 214 | continue 215 | 216 | value = encode( 217 | value, 218 | profondeur = profondeur, 219 | position = position + [balise], 220 | indent = indent 221 | ) 222 | 223 | value_encoded += f"{espacement}{espace}{balise}{value}" 224 | 225 | if not indent: 226 | espace = ' ' 227 | 228 | if indent: 229 | value_encoded += '\n' + ' ' * indent * profondeur 230 | 231 | value_encoded += '>' 232 | 233 | else: 234 | raise TypeError( 235 | 'Type %s non compatible !' % type(data) 236 | + '\nTraceback (index/key):' 237 | + '\n' + '\n'.join( 238 | (' ' * i) + str(value) 239 | for i, value in enumerate(position) 240 | ) 241 | ) 242 | 243 | 244 | return value_encoded -------------------------------------------------------------------------------- /txtcr/core/decode.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Python 3.6.2 3 | # ---------------------------------------------------------------------------- 4 | 5 | from functools import partial 6 | 7 | 8 | from txtcr.core.types import * 9 | from txtcr.core.encode import encode 10 | 11 | 12 | class Conteneur: 13 | 14 | def __init__(self, ancien_conteneur, value): 15 | 16 | self.ancien_conteneur = ancien_conteneur 17 | 18 | self.value = value 19 | 20 | self.__config_add(value) 21 | 22 | # Les objets qui seront dans ce conteneur 23 | # sont d'abors enregistrés en texte 24 | # puis transformés dans self.convert() 25 | self.texte = '' 26 | 27 | self.type = '' 28 | 29 | # Sert à enregistrer la key ou l'index, 30 | # dans le cas de définir la position actuel, 31 | # et pour dans le cas d'un dictionnaire 32 | # enregistrer d'abord la clé pour ensuite 33 | # l'utiliser quand il faudrat mettre la valeur. 34 | self.key = 0 if isinstance(value, (list, tuple)) else '' 35 | 36 | # Retrace tout les keys/indexs précédentes. 37 | self.position = ( 38 | [] if not ancien_conteneur 39 | else 40 | ancien_conteneur.position + [ancien_conteneur.key] 41 | ) 42 | 43 | def convert(self): 44 | 45 | # Convertion texte en type Python 46 | 47 | texte = self.texte 48 | 49 | if self.type == '"': 50 | value = str(texte) 51 | 52 | elif self.type in ['+', '-']: 53 | value = mk_nbr(self.type + texte) 54 | 55 | elif self.type == "'": 56 | value = texte.encode() 57 | 58 | elif self.type in balises_categories: 59 | value = self.type + '#' 60 | 61 | elif self.type == '##': 62 | value = str(texte.strip()) 63 | 64 | if value == 'True': 65 | value = True 66 | elif value == 'False': 67 | value = False 68 | elif value == 'None': 69 | value = None 70 | 71 | self.add(value) 72 | 73 | # Mise à zéro des valeurs pour accueillir le prochain objet 74 | self.texte = '' 75 | self.type = '' 76 | 77 | def __dict_add(self, value): 78 | 79 | if self.key != '': 80 | self.value[self.key] = value 81 | self.key = '' 82 | 83 | else: 84 | self.key = value 85 | 86 | def __list_add(self, value): 87 | self.value.append(value) 88 | self.key += 1 89 | 90 | def __tuple_add(self, value): 91 | self.value += (value,) 92 | self.key += 1 93 | 94 | def __config_add(self, value): 95 | 96 | if isinstance(value, dict): 97 | self.add = self.__dict_add 98 | 99 | elif isinstance(value, list): 100 | self.add = self.__list_add 101 | 102 | elif isinstance(value, tuple): 103 | self.add = self.__tuple_add 104 | 105 | elif is_class(value) and 'repr__' in dir(value): 106 | self.add = self.__dict_add 107 | 108 | 109 | def raise_bad_char_close(carac, conteneur): 110 | 111 | raise ValueError( 112 | 'Mauvais carac fermeture " %s " pour type " %s "' % ( 113 | carac, 114 | conteneur.value.__class__.__name__ 115 | ) 116 | + '\nTraceback (index/key):' 117 | + '\n' + '\n'.join( 118 | (' ' * i) + str(value) 119 | for i, value in enumerate(conteneur.position) 120 | ) 121 | ) 122 | 123 | 124 | def decode(texte, *, exclues=[], ever_list=False): 125 | 126 | echappement = False 127 | conteneur = Conteneur(None, []) 128 | 129 | taille_texte = len(texte) 130 | 131 | for position, carac in enumerate(texte): 132 | 133 | ### Carac spécial 134 | 135 | if carac == '\n' or carac == '\t': 136 | 137 | if conteneur.type == '"' or conteneur.type == "'": 138 | """ 139 | 140 | {"pouet" "paf" 141 | "pomme" "il était une pomme 142 | sur un arbre" 143 | } 144 | 145 | ["pomme"] == "il était une pomme sur un arbre" 146 | 147 | """ 148 | continue 149 | 150 | carac = ' ' 151 | 152 | 153 | ### Ouverture 154 | 155 | if not conteneur.type: 156 | 157 | if carac == ' ': 158 | continue 159 | 160 | is_continue = True 161 | 162 | carac_suivant = ( 163 | '' if taille_texte == position + 1 164 | else 165 | texte[position + 1] 166 | ) 167 | 168 | if carac == '"': 169 | # "Pouet" 170 | conteneur.type = carac 171 | 172 | elif carac == "'": 173 | # 'Pouf' 174 | conteneur.type = carac 175 | 176 | elif carac in ['+', '-'] and carac_suivant in chiffres: 177 | # +3456 -876 178 | conteneur.type = carac 179 | 180 | elif carac in chiffres: 181 | # 765434 182 | conteneur.type = '+' 183 | conteneur.texte += carac 184 | 185 | elif carac == '{': 186 | # {"pouet" 123456} 187 | conteneur = Conteneur(conteneur, {}) 188 | 189 | elif carac == '[': 190 | # ["pouf" "poire" 1234] 191 | conteneur = Conteneur(conteneur, []) 192 | 193 | elif carac == '(': 194 | # (34.6 "wouf" 'pouet') 195 | conteneur = Conteneur(conteneur, ()) 196 | 197 | elif carac == '<': 198 | # 199 | conteneur = Conteneur(conteneur, new_clss(encode)) 200 | 201 | elif carac in balises_categories and carac_suivant == '#': 202 | """ 203 | N#Pouet 204 | I#{} 205 | ... 206 | """ 207 | conteneur.type = carac 208 | 209 | elif carac == '/' and carac_suivant == '/': 210 | """ Commantaire 211 | 212 | {// Information // 213 | ID 123456 214 | // Autre // 215 | langage "fr" 216 | heure "UTC" 217 | } 218 | 219 | """ 220 | conteneur.type = '//' 221 | 222 | elif carac not in [ 223 | '>', '}', ']', ')', # Balises fermante 224 | '#', ',', ':', '=' # Séparations 225 | ]: 226 | conteneur.type = '##' 227 | conteneur.texte += carac 228 | 229 | else: 230 | is_continue = False 231 | 232 | if is_continue: 233 | continue 234 | 235 | 236 | ### Commentaire 237 | 238 | if conteneur.type == '//': 239 | # On remplace le type 240 | # indiquant qu'un commentaire vient d'être créé, 241 | # par le type indiquant qu'on est à l'intérieure d'un commentaire 242 | conteneur.type = '/' 243 | 244 | elif conteneur.type == '/': 245 | # On rentre forcément dans la condition si c'est un commentaire 246 | # car tout est ignoré dedans 247 | 248 | if '/' == carac == texte[position - 1]: # [...] blabla// 249 | conteneur.type = '' 250 | 251 | 252 | ### Texte 253 | 254 | elif ( 255 | not echappement 256 | and ( 257 | carac == conteneur.type == '"' 258 | or carac == conteneur.type == "'" 259 | ) 260 | ): 261 | conteneur.convert() 262 | 263 | 264 | ### Echappement 265 | 266 | elif carac == '\\': 267 | 268 | if echappement: 269 | # "Pomme \\ poire" 270 | echappement = False 271 | conteneur.texte += '\\' 272 | 273 | else: 274 | # "Pomme \ poire" 275 | echappement = True 276 | 277 | # Evite que l'échappement revient à False tout en bas 278 | continue 279 | 280 | elif echappement: 281 | 282 | if carac == 'n': 283 | # "Pomme \n poire" 284 | conteneur.texte += '\n' 285 | 286 | elif carac == 't': 287 | # "Pomme \t poire" 288 | conteneur.texte += '\t' 289 | 290 | 291 | ### Ajout de caractére 292 | 293 | elif ( 294 | conteneur.type == '"' 295 | or conteneur.type == "'" 296 | ): 297 | # "Pomme" 298 | # 'Pomme' 299 | conteneur.texte += carac 300 | 301 | 302 | ### Fermeture conteneur 303 | 304 | elif carac == ')' or carac == ']' or carac == '}' or carac == '>': 305 | 306 | if carac == ')': 307 | if conteneur.value.__class__ != tuple: 308 | raise_bad_char_close(carac, conteneur) 309 | elif carac == ']': 310 | if conteneur.value.__class__ != list: 311 | raise_bad_char_close(carac, conteneur) 312 | elif carac == '}': 313 | if conteneur.value.__class__ != dict: 314 | raise_bad_char_close(carac, conteneur) 315 | elif carac == '>': 316 | if not getattr(conteneur.value.__class__, 'encode__', None): 317 | raise_bad_char_close(carac, conteneur) 318 | 319 | if conteneur.texte: conteneur.convert() 320 | 321 | conteneur.ancien_conteneur.add(conteneur.value) 322 | conteneur = conteneur.ancien_conteneur 323 | 324 | elif carac == '#': 325 | if conteneur.type not in balises_categories: 326 | raise_bad_char_close(carac, conteneur) 327 | 328 | conteneur.convert() 329 | 330 | 331 | ### Chiffres 332 | 333 | elif ( 334 | conteneur.type == '+' 335 | or conteneur.type == '-' 336 | ): 337 | # +123 338 | # -123 339 | if carac not in '0123456789.': 340 | conteneur.convert() 341 | else: 342 | conteneur.texte += carac 343 | 344 | 345 | ### Fin texte sans balise ou ajout carac 346 | 347 | elif conteneur.type == '##': 348 | if ( 349 | carac == ' ' # pouet "pomme" 350 | or carac == ',' # pouet, "pomme" 351 | or carac == ':' # pouet: "pomme" 352 | or carac == '=' # pouet= "pomme" 353 | ): 354 | conteneur.convert() 355 | else: 356 | conteneur.texte += carac 357 | 358 | 359 | if echappement: 360 | echappement = False 361 | 362 | 363 | retour = conteneur.value 364 | 365 | if not retour and not ever_list: 366 | return None 367 | 368 | elif len(retour) == 1 and not ever_list: 369 | return retour[0] 370 | 371 | else: 372 | return retour --------------------------------------------------------------------------------