Manual
El manual de este proyecto se basa en dos partes bien diferenciadas: el manual de uso; y el manual del desarrollador para quienes quieran alterar dicho proyecto para fines específicos dentro de los términos de la licencia que lo sostenta.
Usuario
Para poder utilizar este proyecto simplemente hemos de desargar el archivo Python/PythonMapper.py del proyecto Git. Para evitar problemas con los Path relativos internos del proyecto se aconseja clonar el proyecto.
- typesh
- characters77
- lines3
#!/bin/bash git clone https://git.k3y.pw/KyMAN/PythonMapper cd PythonMapper
Una vez clonado el proyecto, simplemente hemos de crear un archivo JSON dentro del directorio JSON de la raíz del proyecto llamado PythonMapper.py.projects.secrets.json donde instalaremos todos los proyectos con las librerías y propiedades que queramos automatizar a partir de la filosofía de trabajo de la librería PythonMapper. Este archivo JSON se compone de un diccionario donde se mete una clave para identificar cada uno de los proyectos, e internamente al valor de dicha clave, otro diccionario anidado con los siguientes atributos:
- path (Obligatorio): Path absoluto del que colgarán los archivos vinculados al proyecto.
- files (Obligatorio): Lista de los Path relativos al Path absoluto de los archivos Python que queramos mapear.
- map (Obligatorio): Path relativo al Path absoluto del archivo de mapeado resultante.
- dependences (Opcional): Lista de dependencias que queramos agregar a dicho fichero con respecto a los tipados tanto de extensión como de implementación de las clases, así como de los tipados usados por las variables, argumentos, parámetros y/o atributos.
- definitions (Opcional): Lista de variables predefinidas que queramos añadir a nuestro archivo de mapeado para su correcta interpretación. Éste viene siendo una lista sencilla de Strings donde dependiendo de qué se le establece, éste actuará de una forma distinta según ciertos criterios.
- tabulation (Optional): String que representaría los caracteres en blanco usados por tabulación. Por defecto son 4 espacios, pero puede ser establecido 2 espacios en blanco, un caracter de tabulación, etc. A conveniencia del usuario desarrollador.
- timer (Optional): Tiempo de espera para volver a analizar los ficheros para verificar cambios en éstos y reconstruir el archivo de mapeado en milisegundos. Por defecto son 30000 milisegundos.
- rest o rest_timer (Optional): Tiempo de espera para el procesamiento del siguiente fichero a la hora de analizar los ficheros de un proyecto. Este tiempo es bastante importante para evitar un trabajo excesivo sobre el procesador permitiendo un descanso en milisegundos entre el procesado de un archivo y otro. Por defecto, el tiempo de espera es de 10 milisegundos.
Es importante destacar que los Paths que se usarán para la autogeneración del mepeado parten del path dado en la configuración concatenado mediante un Slash (/) al Path relativo que se esté trabajando, ya sea el del propio mapeado como el de cualquiera de los ficheros dados, por lo que es aconsejable no acabar con Slash el parámetro path; así como tampoco iniciar con dicho caracter ninguno de los ficheros definidos en files ni el Path relativo del mapeado establecido en map.
La ejecución de la librería Python/PythonMapper.py puede ser ejecutada desde un entorno Unix desde el fichero Tools/run.sh del proyecto en cuestiónk, aunque puede ser también ejecutado directamente desde Python. Dicho archivo Bash Shell está diseñado para simplificar y facilitar la ejecución del mismo.
Dependencias
La lista de dependencias no es más que una lista cuyos valores pueden depender acerca de la finalidad o el sistema de importar dicha dependencia. Dichos patrones serían los siguientes:
Dependencia directa
La dependencia directa no es más que un String el cual será interpretado como código Python de forma directa. Se distingue de la opción de establecer el nombre de la dependencia por la existencia de espacios en dicho String que complementa el Script Python.
- typejson
- characters111
- lines7
{ "AnP" : { "dependences" : [ "from re import Pattern as REPattern" ] } }
- typepy
- characters85
- lines5
#!/usr/bin/env python # -*- coding: utf-8 -*- from re import Pattern as REPattern
Dependencia por nombre
La dependencia por nombre no es más que un String el cual contiene el nombre de la dependencia que queremos adjuntar. A diferencia de la dependencia directa, ésta no puede contener espacios y se espera el nombre o el Path Python de la dependencia separado por puntos, sin espacios y sin aliases.
- typejson
- characters84
- lines7
{ "AnP" : { "dependences" : [ "datetime" ] } }
- typepy
- characters65
- lines5
#!/usr/bin/env python # -*- coding: utf-8 -*- import datetime
Dependencia parcial
La dependencia parcial consta de una lista de 2 elementos donde el primer elemento es un String que determina el Path Python del paquete de la dependencia; y el segundo elemento puede ser un String o una lista de Strings para determinar uno o más elementos a importar, específicamente de dicha librería, ahorrando una importación total, siendo ésta más definida y exclusiva al uso.
- typejson
- characters154
- lines8
{ "AnP" : { "dependences" : [ ["typing", ["Any", "Optional", "NewType"]], ["threading", "Thread"] ] } }
- typepy
- characters120
- lines6
#!/usr/bin/env python # -*- coding: utf-8 -*- from typing import Any, Optional, NewType from threading import Thread
Importación con alias
La importación con alias se crea a partir de una lista de 3 elementos String los cuales son:
- Path Python del paquete de la dependencia.
- Elemento que queremos importar.
- Alias que le queremos dar.
- typejson
- characters145
- lines8
{ "AnP" : { "dependences" : [ ["re", "Pattern", "REPattern"], ["re", "Match", "REMatch"] ] } }
- typepy
- characters117
- lines6
#!/usr/bin/env python # -*- coding: utf-8 -*- from re import Pattern as REPattern from re import Match as REMatch
Definiciones
Las definiciones se usan para definir variables que ayuden a la interpretación correcta del mapeado si alguna dependencia no es aplicable desde un principio o en el conjunto del mapeado. La idea es crear temporal o permanentemente dentro del mapeado las variables de uso para tipados. Éstas pueden ser de las siguientes formas:
Las definiciones no son variables con un tipado concreto, sino que es la definición de un tipado a usar dentro del mapeado.
Si en el mapeado de Python no se integra la importación de typing.NewType, éste lo implementará automáticamente si hay alguna variable a crear.
Definición por nombre
Cuando se define una variable por nombre, ésta quedará con un tipado equivalente de Any mediante su nombre.
- typejson
- characters79
- lines7
{ "AnP" : { "definitions" : [ "AnP" ] } }
- typepy
- characters103
- lines7
#!/usr/bin/env python # -*- coding: utf-8 -*- from typing import NewType AnP = NewType("AnP", Any)
Definición cruda
La definición cruda viene siendo un String el cual genere la definición de por sí.
- typejson
- characters103
- lines7
{ "AnP" : { "definitions" : [ "AnP = NewType(\"AnP\", Any)" ] } }
- typepy
- characters75
- lines5
#!/usr/bin/env python # -*- coding: utf-8 -*- AnP = NewType("AnP", Any)
¡IMPORTANTE!Si se hace una definición cruda se entiende que toda dependencia que requiera está implementada en la sección de dependencias. si no es así, la librería de mapeado dará error.
Este tipo de definiciones se aplican a tipados que requieren de especificar conceptos de tipado concretos.
Ejemplo
Un ejemplo de un proyecto a mapear sería el propio AnP, el cual, a fecha de la creación de dicha documentación sería de la siguiente forma:
- typejson
- characters1970
- lines57
{ "anp" : { "path" : "/media/kyman/SSD2TB/git/AnP/Python", "files" : [ "Models/HTTP.py", "Models/RoutesResponse.py", "Abstracts/Applications.py", "Models/Routes.py", "Modules/ErrorsManager.py", "Abstracts/Managers.py", "Abstracts/OwnSettings.py", "Abstracts/HTTP.py", "Application/Events.py", "Models/Threads.py", "Models/Commands.py", "Abstracts/Connections.py", "Abstracts/Controllers.py", "Managers/PrintTypes.py", "Application/Path.py", "Managers/Settings.py", "Managers/I18N.py", "Modules/WMarkDown.py", "Models/Licenses.py", "Managers/Licenses.py", "Application/Threads.py", "Application/Terminal.py", "Managers/MimeExtensions.py", "Managers/Connections.py", "Managers/Views.py", "Managers/Controllers.py", "Managers/Routes.py", "Managers/Servers.py", "Managers/Applications.py", "Models/Shell.py", "Models/Proxy.py", "Drivers/UnixDomainSocketWebServer.py", "Drivers/Shell.py", "Drivers/Curl.py", "Drivers/CSV.py", "Drivers/Selenium.py", "Drivers/ProxiesList.py", "Application/AnP.py" ], "map" : "Abstracts/AnPMap.py", "dependences" : [ ["typing", ["Any", "Optional", "Callable"]], ["socket", "socket", "Socket"], "datetime", ["re", "Pattern", "REPattern"], ["re", "Match", "REMatch"], ["threading", "Thread"] ], "definitions" : [ "Self", "WebDriver", "WebElement", "AnP", "class ObjectSetter:pass" ] } }
Manual tećnico
El sistema se basa únicamente en una librería Python la cual ha de ser ejecutada tras establecer la configuración del o los proyectos que queramos mapear. Una vez está ejecutado con dicha configuración, ésta creará un objeto PythonMapper.Project para gestionar los proyectos individualmente. El hilo de procesos que gestiona dicho proyecto es único y colgarán de éste un sistema de eventos similares a los Intervalos de JavaScript, el cual gestionará tiempos de espera, de carga y procesamiento en monohilo para evitar un consumo de recursos excesivo y permitir la convivencia de dicho proceso en entornos con más procesos dentro del ecosistema Software donde se aloje.
La configuración que se extrae para gestionar los valores de los objetos PythonMapper.Project serán sacados principalmente del diccionario dado para cada proyecto individualmente, y se complementará a partir de la clase estática PythonMapper.Settings, la cual contiene la configuración por defecto para todo el proyecto.
Por otro lado, tenemos un sistema de gestión de memoria anidada mediante la clase objeto PythonMapper.SearchData, contiene los datos necesarios para la gestión de cada una de las librerías del proyecto qeu se esté trabajando, ordenando sus recursos por niveles de dependencias y de contenidos.
Finalmente, el proyecto hace uso de la clase objeto PythonMapper.ClassData que contiene toda la información de cada uno de los niveles de clase de una librería, siendo el nivel 0 el Root de la propia librería y los siguientes niveles las clases, incluyendo las clases anidadas. También contiene los elementos estáticos, variables de objeto y métodos. Cada una de las clases anidadas estará compuesta por otro objeto PythonMapper.ClassData anidado sobre el anterior para establecer orden de dependencias.
Para poder gestionar de una forma óptima dicho contenido, todo se moverá sobre un objeto de la clase PythonMapper, el cual cargarálos proyectos y los asignará a objetos de tipo PythonMapper.Project gestionados a partir de un único hilo que funciona como un Intervalo JavaScript para gestionar los tiempos mediante simulaciones de Wait con tiempos no definidos para su trabajo tanto en la configuración como con la capacidad de poderse cerrar prácticamente instáneamente la aplicación, con una gestión de cola o de semáforo sobre un único hilo rápido.
Para hacer más efectivo el proceso, éste cacheará todos los archivos que ha de mapear, donde detectará si cada archivo fue alterado o no, donde si lo fue, éste se procesará y creará dará orden a actualizar el archivo de mapeado una vez termine de procesar todos los archivos.
El proceso de los archivos, para simplificar los patrones regulares y las estructuras de búsqueda, se formatearán mediante claves que identifican las anidaciones, mediante el método estático PythonMapper.Project.unclean_data. Tras dicho paso se Matchearán los patrones que identifican las clases, las variables tanto estáticas como de objeto, y los métodos, y se ignorarán los elementos privados y/o repetidos, quedándose sólo los elementos ocultos o públicos en su primera aparición. La gestión de los Matcheos se hace a partir de una estructura While True que gestiona el contenido hasta que éste sea finalizado.
Todos estos resultados se almacenan en la estructura anidada de tipo PythonMapper.ClassData para su posterior gestión. Cada vez que se detecta un fragmento a almacenar en dicha estructura, éste se le limpiará el formato mediante el método estático PythonMapper.Project.clean_code, y desprovisto de las tabulaciones iniciales para ser establecidas limpiamente después, listo para ser integrado dentro del archivo Mapper. Los niveles de anidamiento de la estructura de tipo PythonMapper.ClassData determinarán la tabulación.
El orden de los elementos dentro del archivo mapeado será el dado por los archivos, aunque habrá un orden establecido para los elementos integrados en una clase donde se establece la definición de nombre de clase, luego los elementos estáticos, luego el constructor con sus correspondientes variables objeto si éste los tuviese, y finalmente los métodos del mismo, incluyendo sus modificadores.