Está en la página 1de 32

LINEAMIENTOS DE ARQUETIPO

PYSPARK

CHAPTER DATA ENGINEERING


COE DATA
v.1.1
CONTROL DE VERSIONES
Versión Fecha Descripción Autor Revisado por

✓ Anthony Cachay
V.1.0 01/10/2021 Lineamientos base Max Morales
✓ Erick Salazar

✓ Anthony Cachay
V.1.1 11/01/2022 Parametria Max Morales
✓ Erick Salazar
ÍNDICE Pág.
1. Prefacio ............................................................................................................................... 5
2. Formatos para desarrollo PySpark.............................................................................. 6
3.1. FORMATO FDP01 – Flujo de desarrollo ................................................................. 6
3.2. FORMATO PDP02 – Testing ...................................................................................... 6
3. Jerarquía de desarrollo................................................................................................... 6
4.1. Estructura de carpetas ................................................................................................ 6
4.2 Archivos de configuración ........................................................................................ 12
4.3. Formatos permitidos ................................................................................................. 12
4.4. Tamaño de archivos .................................................................................................. 13
4. Logging ............................................................................................................................. 13
5.1. Criterios básicos......................................................................................................... 13
a) Instanciar la clase Logger .................................................................................... 13
b) No enviar correos electrónicos. ......................................................................... 13
c) Estándar básico de configuración logging. .................................................... 13
d) Métodos permitidos para registro log4j. .......................................................... 14
5.2. Seguridad de logging. ............................................................................................... 14
5. Testing............................................................................................................................... 18
6.1. Pruebas unitarias. ...................................................................................................... 18
a) Reducir datos de prueba al mínimo necesario............................................... 18
b) Consideraciones para la realización de pruebas unitarias ......................... 18
6. Clean Code ....................................................................................................................... 20
7.1. Reglas generales ........................................................................................................ 20
a) Seguir la convención estándar .............................................................................. 20
b) Mantener simple el desarrollo................................................................................ 20
7.2. Reglas de diseño. ....................................................................................................... 22
a) Mantener los datos de configuración en alto nivel .......................................... 22
b) Priorizar el polimorfismo antes que la estructura if/else ................................ 22
7.3. Reglas de nombres. ................................................................................................... 22
a) Utilizar nombres descriptivos ............................................................................. 22
b) Módulos y paquetes .............................................................................................. 23
c) Variables y propiedades ....................................................................................... 23
d) Funciones y métodos ............................................................................................ 25
e) Clases ........................................................................................................................ 26
f) Evitar nombres genéricos. ................................................................................... 26
g) Mantener la consistencia. .................................................................................... 27
h) Utilizar nombres pronunciables. ........................................................................ 27
i) Reemplazar los números mágicos .................................................................... 27
j) Evitar las codificaciones. ..................................................................................... 27
7.4. Reglas de funciones y métodos ............................................................................. 27
a) Utilizar nombres descriptivos ................................................................................ 27
b) Mantener las funciones pequeñas ........................................................................ 27
c) Las funciones deben hacer una sola cosa ......................................................... 28
d) No usar argumentos bandera................................................................................. 28
7.5. Reglas de comentarios ............................................................................................. 29
a) Evitar la redundancia en los comentarios .......................................................... 29
b) Si el código es complejo agregar ejemplos de uso ......................................... 29
c) Agregar advertencias ............................................................................................... 29
d) Los argumentos de los métodos deben describirse ....................................... 30
e) La documentación de código está permitida en español o en inglés ......... 30
1. Prefacio

El Chapter Data Engineering con el fin de mejorar los desarrollos actuales de


PySpark presenta este documento de lineamientos, los cuales tienen un
estándar alineado a las buenas prácticas internacionales tanto a nivel técnico y/o
funcional. Dentro de este documento se consideran formatos para desarrollo,
Jerarquía de desarrollo, Logging, Testing, Clean Code y Devops.

Asimismo, es de conocimiento que existen rutinas que actualmente se


encuentran desarrolladas directamente en Pyspark (sin un framework de
desarrollo). Para estas rutinas se recomienda seguir los lineamientos detallados
en el documento.

Este documento está sujeto a cambios, por lo cual presenta un control de


versiones.

Por seguridad de la información está totalmente la distribución o copia de este


documento bajo sanción administrativa, jurídica y/o legal según sea el caso
dentro de la normativa del BCP.
2. Formatos para desarrollo PySpark
3.1. FORMATO FDP01 – Diseño técnico
Para descargar el formato ingresar al siguiente link:

Link FDP01

El formato debe ser llenado con la siguiente información:

a) Lista de inputs y outputs de nuestro proceso


b) Volumetría y peso estimada de los inputs
c) Frecuencia de ejecución
d) Tipo de relación/operación entre las tablas (dependencias)

3.2. FORMATO PDP02 – Testing


Para descargar el formato ingresar al siguiente link:

Link PDP02

El formato debe considerar lo siguiente:

a) Ejecución de pruebas unitarias. Captura del estado de las


pruebas unitarias de cada uno de los métodos de las clases o
funciones, ya sean públicas, privadas y protegidas.
b) Cobertura de Código. Reporte de la cobertura de código a nivel
de paquetes, módulos, clases y líneas de código.

3. Jerarquía de desarrollo
4.1. Estructura de carpetas
Para definir la estructura de carpetas de nuestro desarrollo debe estar
implementado de forma modular para garantizar una mantenibilidad a futuro
y tener un orden de trabajo.
A) Source Folder
La jerarquía de carpetas que debe tener nuestro
desarrollo debe comenzar por definir un source
(src), dentro de esta carpeta debe tener una
carpeta main y tests.

A este nivel debe crear el README y el SETUP


del proyecto.

El readme es un archivo informativo, donde se debe escribir lo siguiente:

a1) README.md

Título del proyecto

Descripción de la funcionalidad: Origen y Destino de


tablas

Control de versiones: Se debe llenar una tabla con los siguientes


datos: Versión, Desarrollador, Empresa, PO, Fecha

Requisitos: Se debe mencionar los requerimientos del aplicativo,


como versión de librerías o extras.

Setup: Se debe mencionar los pasos para empaquetar e instalar el


aplicativo en local, así como también que variables de entorno se
deben configurar.

Testing: Se debe mencionar el comando para correr las pruebas


unitarias.

Run: Se debe colocar el comando para ejecutar el proyecto tanto


en local como en desarrollo.

Otros (opcional): Se puede colocar el tema de la licencia del


aplicativo, contribuidores o visto buenos según sea el caso.

Este template se puede descargar del repositorio de ejemplos.


a2) setup.py

El setup es un archivo de configuración que permite al aplicativo indicar la forma


como debe empaquetarse, se debe tener configurado de manera obligatoria lo
siguiente:

import os

from setuptools import setup


from pyspark_archetype.common.packages import custom_find_packages

readme = open(
os.path.join(os.path.dirname(__file__), "README.md"),
"r", encoding="utf-8"
)

setup(
name="ARTIFACT_NAME",
version="$version",
author="$empresa",
description="$comentarios",
long_description=readme.read(),
long_description_content_type="text/markdown",
package_dir={
'': 'main'
},
packages=custom_find_packages('src', exclude=["tests"]),
extras_require={
"dev": ["check-manifest"],
"test": ["coverage"]
},
classifiers=[
"Programming Language :: Python :: 3",
"License :: BCP License"
],
python_requires='>=3.6.x',
license="Apache-2.0",
platform="UNIX"
)

readme.close()

Se debe importar la función custom_find_packages, esto permitirá


indicar que tenemos un ejecutable pyspark.

En la parte de name, debe estar configurado en "ARTIFACT_NAME", al


momento de subir a producción.

Llenar los datos de versión del aplicativo, autor que se debe colocar el
nombre del desarrollador a cargo y la empresa a donde pertenece, en
descripción debe ser un resumen base del aplicativo.
Si se quiere agregar otras configuraciones que no están dentro de este
archivo de configuración se debe conversar con su CL y coordinar con el equipo
de datos.

A3) Main Folder

Dentro del folder Main almacenaremos


nuestras lógicas de nuestra aplicación.

Primero se debe definir el nombre del


módulo del proyecto: Ej. example, seguido
del archivo __init__.py que identifica
nuestra carpeta como módulo.

ejecutable. Dentro configuramos el nombre


del módulo, en este caso example y
referenciamos al app_job.

Transformation. Tercero como recomendación crear nuestra carpeta,


módulo y clase donde estará nuestra lógica de
nuestra aplicación.

Dentro de example.py podemos instanciar la


clase Reader y Writer que son componentes
del arquetipo para facilitar la lectura y escritura.

También podemos instanciar otras


funcionalidades que nos permitirá aún más, mejorar nuestro desarrollo.
from pyspark_archetype.parameters.process_parameter import Joins
from pyspark_archetype.transformation.reader import Reader
from pyspark_archetype.transformation.writer import Writer

class Transformation(Joins):
"""
Template to do transformation
"""

def __init__(self) -> None:


"""
Instance from Reader and Writer
"""
self._reader = Reader()
self._writer = Writer()

def transformer(self) -> None:


""" Example
from pyspark_archetype.common.dicts import LazyDict

example_df = self._reader.read(self._table_input)

dfs = LazyDict({"table_output": example_df}])


self._writer.write(dfs)
"""

pass

Para la clase Transformation es obligatorio sobrescribir el método


transformer.

app_job.py Cuarto, dentro de la misma línea del módulo se creará el Job que
permite integrar y correr el proceso.

from pyspark_archetype.job_init import Main


from pyspark_archetype.parameters.config import Config
from example.transformation.example import Transformation

_sys = __import__("sys")

def main():
args = _sys.argv
Config.SPARK_CONFIG_NAME = '$SPARK_CONFIG_NAME'
Config.PROCESS_CONFIG_NAME = '$PROCESS_CONFIG_NAME'

Main().run_process(args, Transformation)

Se debe importar la clase Main y Config del arquetipo para que este le
permita inyectar nuestras transformaciones hechas en nuestro aplicativo.
A la clase Config se debe sobrescribir las variables de los nombres de los
json de la parametría de spark y configuración del proceso.

Al método run_process se debe pasar como parámetros los argumentos y


nuestra clase Transformation

a4) Tests Folder.

Dentro de este folder se debe crear nuestras pruebas


unitarias, acá debemos crear la misma jerarquía de
paquetes y módulos definidas en nuestro main. Muy
importante que nuestros módulos deben comenzar con
test_ seguido del nombre original.

** Para los lineamientos ir a la sección 5 de Testing. **

B) Scripts Folder
En este folder se debe colocar nuestras
DML del proyecto a ejecutar en los tres
ambientes.

Obligatorio se deben crear las siguientes


variables:

TUNING BASE + PARAMETROS ARQUETIPO:


{
"spark.hadoop.hive.exec.dynamic.partition.mode": "nonstrict",
"spark.pyspark.python": "/usr/local/bin/python3.6",
"spark.files": "ARCHETYPE_PATH/log4j.properties",
"spark.driver.extraJavaOptions": "-Dlog4j.configuration=file:ARCHETYPE_PATH/log4j.properties",
"spark.pyspark.virtualenv.enabled": "true",
"spark.pyspark.virtualenv.type": "native",
"spark.pyspark.virtualenv.bin.path": "PROJECT_PATH/VIRTUALENV_PATH/bin/",
"spark.pyspark.driver.python": "PROJECT_PATH/VIRTUALENV_PATH/bin/python3",
"sparkSubmit.mainPackage": "PROJECT_PATH/VIRTUALENV_PATH/lib/python3.6/site-packages/app_job.py",
"PRM_SPARK_ENVIRONMENT": "ENVIRONMENT",
"PRM_SPARK_PATH_PARAMETERS": "PROJECT_PATH/JSON_CLUSTER"
}
C) Devops Folder
En este folder se debe configurar los
archivos Groovy para hacer el
despliegue con Jenkins en los
diferentes ambientes. *

* Estos templates se pueden


descargar del repositorio de ejemplos.

4.2 Archivos de configuración


Para los archivos de configuración a nivel desarrollo local se debe definir una
carpeta dentro de tests, con la carpeta resources en la cual se tendrá las
siguientes carpetas:

• inputs: Tablas con archivos de data dummy para realizar pruebas


a nivel local.
• outputs: Tablas de salida para realizar pruebas. # Esto colocar en
el gitignore antes de subir a bitbucket.
• log4j.properties: Configuración base para realizar nuestra
trazabilidad a nivel de logs de auditoria.
• JSON_CFG_APP: Parametría Spark
• JSON_PRM_APP: Parametría de configuración

4.3. Formatos permitidos


Los formatos permitidos para realizar pruebas a nivel local son los siguientes:

✓ Parquet
✓ Avro
✓ Csv
✓ Txt
✓ Otros*

* Cualquier otro formato que no este dentro de esta lista se sebe coordinar
con el equipo de datos.
4.4. Tamaño de archivos
El tamaño de archivos permitidos para realizar pruebas a nivel local no debe
supera 5Mb de información.

“Recordemos que el formato parquet logra una razón de compresión


de hasta el 97.56 %, lo que significa que 1 MB de datos en formato
parquet se traduce en 40 MB de datos en algún formato sin
compresión”.

4. Logging
5.1. Criterios básicos
a) Instanciar la clase Logger
Podemos usar el componente de Logger, importando la clase:

from pyspark_archetype.bootstrap.Logger import Logger

Logger().logger.info("testing")

* Para usar la clase logger es importante que antes se haya definido la sesión de
Spark

b) No enviar correos electrónicos.


Se puede usar SMTPAppender para notificar las excepciones en los logs
a través de correos electrónicos. Pero para la definición de este
documento no está permitido.

c) Estándar básico de configuración logging.


Se recomienda utilizar filtros para suprimir mensajes de log específicos. A
continuación, se muestran los detalles de configuración de
log4j.properties para configurar filtros con el fin de suprimir ciertas
declaraciones de log.
# Root logger option
log4j.rootLogger=DEBUG, stdout

# Direct log messages to stdout


log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %-15.15c{1}:%4L : %m%n
log4j.appender.stdout.encoding=UTF-8

d) Métodos permitidos para registro log4j.


Se puede especificar en las propiedades de log4j qué niveles de registro de
log4j desea registrar. Pero los métodos permitidos son los siguientes:

▪ logger.info(message)
▪ logger.warn(message)
▪ logger.error(message)
▪ logger.debug(message)

5.2. Seguridad de logging.


A continuación, se detallan mecanismo para el log de aplicaciones
relacionados con el registro de seguridad.

1. Atributos de eventos.

Cada entrada de log debe registrar lo siguiente para cada evento:

“Cuándo”:

• Fecha y hora del log (formato internacional).


• Fecha y hora del evento: la marca de tiempo del evento puede ser
diferente a la hora del registro, por ejemplo: registro del servidor puede
ser de forma continua o intermitente.

“Dónde”:

• Identificador de la aplicación, por ejemplo: nombre y versión.


• No está permitido imprimir rutas de aplicación en producción.

“Quién” (usuario humano o de máquina):

• Identidad del usuario (Matrícula de Red).


“Qué”:

• Tipo de evento: error, info, warning, debug


• Gravedad del evento, nivel del logging: INFO, WARN, ERROR,
DEBUG

Ejemplo:

2021-10-13 09:25:16 DEBUG < TEST >: ADMIN: Listener Metrics: {'task_number': '1', 'output_size_bytes': '2198', 'rows_number': '3'}

Cuando: 2021-10-13 09:25:16


Dónde: < TEST >
Quién: ADMIN
Qué: DEBUG

2. ¿Dónde se registrarán los logs?

Los registros de log se registran en la ruta Linux:

RDV:

<AMBIENTE>/<ORGANIZACION>/LOG
UDV/DDV/EDV

<AMBIENTE>/<ORGANIZACION>/<CAPA>/<UNIDAD>/<PROYECTO>/LOG

3. ¿Qué eventos están permitidos registrar?

Está permitido registrar lo siguiente:

• Fallos de validación de input, por ejemplo: violaciones de protocolo,


codificaciones inaceptables, nombres y valores de parámetros no
válidos.
• Fallos de validación de output, por ejemplo: el conjunto de registros
de la base de datos no coincide, la codificación de datos no es válida.
• Éxitos y fallos de autenticación.
• Fallos de autorización (control de acceso).
• Fallos en la gestión de sesiones.
• Errores de aplicación y eventos del sistema, por ejemplo: errores de
sintaxis y tiempo de ejecución, problemas de conectividad, problemas
de rendimiento, mensajes de error de servicios de terceros, errores del
sistema de archivos, detección de virus de carga de archivos, cambios
de configuración.
• Arranques y cierres de aplicaciones y sistemas relacionados, e
inicialización del registro (inicio, parada o pausa).
• Uso de funciones de alto riesgo (Solo estatus, no grabar datos en
duro), por ejemplo: conexiones de red, creación o eliminación de
usuarios, cambios de privilegios, asignación de usuarios a tokens,
creación o eliminación de tokens, uso de privilegios administrativos de
sistemas, acceso de administradores de aplicaciones, todas las
acciones de usuarios con privilegios administrativos, acceso a datos
de titulares de tarjetas de pago, uso de claves de cifrado de datos,
cambios de claves, creación y eliminación de objetos a nivel del
sistema, importación y exportación de datos, incluidos informes
basados en pantallas, envío de contenido generado por el usuario,
especialmente cargas de archivos.

** Eventos que no estén contemplados en este documento será


rechazados.

4. Datos que excluir:

Los datos mostrados a continuación no deben registrarse directamente en los


logs, sino deberían eliminarse:

• Código fuente de la aplicación.


• Valores de identificación de sesión.
• Cadenas de conexión de base de datos.
• Claves de cifrado y otros secretos maestros.
• Datos del titular de la cuenta bancaria o de la tarjeta de pago.
• Datos DAC
• Los datos de una clasificación de seguridad superior a la que se
permite almacenar el sistema de registro.
• Rutas de archivo.
• Nombres y direcciones de redes internas.
5. Testing
El objetivo de las pruebas es prevenir que el error llegue al entorno de
producción y despliegue. Estas se caracterizan por corresponder a una
funcionalidad específica del código, ser legibles, rápidas de ejecutar y
repetibles (ejecutar múltiples veces) e independientes.

Se recomienda usar el desarrollo guiado por pruebas o TDD por sus siglas
en inglés (Test Driven Development) es un proceso que da las pautas para
el desarrollo de pruebas en el código de producción.

5.1. Pruebas unitarias.


Las pruebas unitarias permitirán validar que cada unidad de código se
comporte como es esperado. Las pruebas unitarias buscan validar que los
métodos o funciones cumplan su cometido independientemente de la
entrada provista.

Para cumplir con el estándar de pruebas se debe realizar lo siguiente:

a) Reducir datos de prueba al mínimo necesario


Las pruebas unitarias son de bajo nivel y serán ejecutadas en el servidor
de integración continua.

Este objetivo puede cumplirse de dos maneras:

1. Realizando un muestreo aleatorio sobre los datos utilizados.

2. Convirtiendo los datos a formato Dummy.

b) Consideraciones para la realización de pruebas unitarias


• Las pruebas unitarias deberán ser realizadas sólo con Pytest +
Mockito(Según sea el caso).
• Desarrollar pruebas unitarias por cada método o función de las
clases.
• Si un método se modifica en su implementación, evaluar el
resultado en la prueba unitaria y modificar de ser necesario.
• Los paquetes deben tener la misma estructura de las clases
principales src/main/project.
• Las pruebas unitarias deben tener el mismo nombre de la clase
comenzando con el sufijo: “test_”.
• Realizar Debug de las pruebas unitarias para validar que no existan
falsos positivos.
• Verificar que la cobertura de código sea mayor o igual al 80%. Para
realizar la cobertura se utilizará la librería Coverage o el reporte de
PyCharm.
• No está permitido usar la función: collect().
• Las transformaciones deben mantenerse en unidades pequeñas de
desarrollo para poder testear cada parte del código, es decir si en
una transformación se tiene joins, selects, groups o filters estos
deben ser separados para poder realizar la prueba unitaria sobre
una porción de la transformación y no sobre la transformación
completa.
• Toda prueba unitaria debe crear sus datos Dummies para probar
los métodos de la clase con solo la data necesaria para generar el
test unitario para ello debe tener un paquete dummy como se ve en
el siguiente ejemplo.

def test_join_moneda_cuenta(transformation):
dummy_data = [
Row(
codmoneda="1001",
codproducto="FCTCBT",
fecvcto=datetime(2017, 4, 27),
nbrmoneda="DOLAR AMERICANO EE.UU",
fecrutina=datetime(2021, 2, 13)
)
]
dummy_df = SparkEntryPoint().spark.createDataFrame(dummy_data)
dummy_cols = dummy.schema.names

result = transformation._Transformation__join_moneda_cuenta(
transformation._Transformation__cuenta_df,
transformation._Transformation__moneda_df
)

assert result is not None


assert result.count() == 10
assert result.schema.names.sort() == dummy_df.schema.names.sort()
assert result.select(dummy_cols).take(1) == dummy_data
6. Clean Code
Clean Code se trata de escribir código que sea entendible, fácil de leer y fácil
de mantener.

6.1. Reglas generales


a) Seguir la convención estándar
Los lenguajes de alto nivel tienen convenciones que nos facilitan la
escritura de código.

Se utilizará la convención PEP8 correspondiente para Python y la


reglas de SonarQube.

b) Mantener simple el desarrollo


Se debe reducir la complejidad tanto como sea posible.

Notación Big O Nombre Nivel de complejidad


O(1) Constante Muy bueno
O(log n) Logarítmico Bueno
O(n) Lineal Razonable
O(n log n) Lineal/logarítmico Malo
O(n^2) Cuadrática Muy malo
O(n^3) Cúbica Muy malo
O(2^n) Exponencial Muy malo
O(n!) Factorial Muy malo

Los algoritmos que se desarrollen se deben de optimizar como mínimo a un


nivel de complejidad “razonable”, no utilizar más de tres iteraciones. A
continuación, se presenta un ejemplo donde se reemplaza la iteración con
recursividad para hacer más eficiente la compilación del código.

Función para hallar el máximo común divisor utilizando el bucle while:

def greatest_common_divisor(a: int, b: int) -> None:


while a != b:
if a > b:
a -= b
else:
b -= a
Se reemplaza el bucle utilizando recursividad:

def greatest_common_divisor(a: int, b: int) -> Any:


if a > b:
return greatest_common_divisor(a - b, b)
elif b > a:
return greatest_common_divisor(a, b - a)
else:
return a

Es recomendable cambiar bucles for, por iteradores (Map, Filter, Reduce)

Comparación de tiempos: For vs Map

from timeit import timeit

_sum = lambda i: i + 1

def _for() -> None:


for i in range(1000):
_sum(i)

def _map() -> None:


map(_sum, range(1000))

def _list() -> None:


[_sum(i) for i in range(1000)]

print(timeit(_for, number=10000)) # 2.5515936921388516


print(timeit(_map, number=10000)) # 0.010167432629884843
print(timeit(_list, number=10000)) # 3.090125159839033

Si la lógica es muy dificultosa de debe sustentar el uso de for o while.


6.2. Reglas de diseño.
a) Mantener los datos de configuración en alto nivel
Si se tiene una constante como valor por defecto o de configuración que
es conocida y esperada en un nivel alto de abstracción, no se debe
colocar en una función de bajo nivel. Se debe exponer como un
argumento de la función de bajo nivel llamada desde la función de alto
nivel.
CORRECTO
INCORRECTO class Example:
class Example:
def __init__(self) -> None:
def __process_path(self) -> None: self.__PATH__ = "dummy"
path = "dummy"
path.upper() def __process_path(self) -> None:
self.__PATH__.upper()

b) Priorizar el polimorfismo antes que la estructura if/else


INCORRECTO CORRECTO
class Example: class Europe:
def get_speed(self, continent: str) -> int:
if continent == "Europe":
def get_speed(self) -> int:
return 100 return 100
if continent == "America":
return 4 * 5 * 100
class America:

def get_speed(self) -> int:


return 2000

6.3. Reglas de nombres.

Nombrar cosas (variables, propiedades, funciones, métodos, clases)


correctamente y de una manera comprensible es una parte extremadamente
importante de escribir código limpio, nombrar adecuadamente permite
comprender el código sin entrar en detalle. A continuación, se describen
reglas que ayuden a elegir nombres adecuados.

a) Utilizar nombres descriptivos


Los nombres deben describir lo que almacena una variable o propiedad o
lo que hace una función o método. O qué tipo de objeto se creará al crear
una instancia de una clase. Deben ser precisos, descriptivos y sin
ambigüedades.

Tipo Estándar

Constantes UPPER_SNAKE_CASE
Variables lower_snake_case
Funciones lower_snake_case
Clases UpperCamelCase
Módulos lower_snake_case
Paquetes lower_snake_case

Para las variables o métodos protegidos se debe anteponer el guion abajo


_protected.

Para las variables o métodos privados se debe anteponer el doble guion


abajo __private.

b) Módulos y paquetes
Los módulos deben tener nombres cortos y en minúsculas. Se pueden
usar guiones bajos en el nombre del módulo si esto mejora la legibilidad.
Los paquetes en Python también deben tener nombres cortos en
minúsculas, aunque no es recomendable el uso de guiones bajos.

c) Variables y propiedades
Las variables y propiedades normalmente deben recibir un sustantivo
como nombre, que indique qué tipo de dato está almacenando.

Ejemplo: user, product, costumer, database, transaction, etc.

También se puede utilizar una frase corta con un adjetivo, especialmente


para almacenar valores booleanos.

Ejemplo: is_valid, did_authenticate, is_logged, email_exists, etc.

A continuación, se presenta un listado de sufijos los cuales debes utilizar


si las variables o constantes están dentro de este concepto:
Nombre de sufijo Nombre de sufijo
Descripción
(inglés) (español)
config configuracion Para leer una configuración
df df Todo DataFrame que sea
utilizado como lectura
field campo Se aplica sobre las variables
que hacen referencia a un
campo
path ruta Hace referencia a una ruta de
un origen
tag_name etiqueta_nombre Si se refiere a un etiqueta
column_types tipo_columna Si se refiere a los tipos de
col_types tipo_col datos de las columnas
Columna columna Si se refiere a una columna en
col col específico se debe utilizar
column, también se puede
utilizar su plural
column_name nombre_columna Si se refiere al nombre de la
nombre_col columna
id id Si se refiere a una constante
code cod como identificador
cod codigo
file archivo Si se tiene un file y se desea
referir a este utiliza este sufijo
percentage porcentaje Se refiere al uso de porcentaje
description descripción Para descripción de variables
desc desc
amount monto Cuando se refiere a un importe
date fecha Cuando se refiere a una fecha
number numero Cuando se refiere a una
num num cantidad
type tipo Si se necesita hacer referencia
de algún tipo en específico que
permite realizar el casteo en un
field puedes utilizar field. Por
ejemplo:
type_decimal
type_date

d) Funciones y métodos
Las funciones y métodos realizan tareas y operaciones, por lo tanto,
normalmente deben recibir un verbo como nombre.

Ejemplo: login(), create_user(), database_insert(), log(), etc.

Se debe evitar utilizar nombres como: email(), user(), etc. Estos nombres
pueden confundirse como variables, en su lugar es mejor utilizar
get_email(), etc.

En ciertos casos, las funciones y los métodos también se pueden usar


para producir valores principalmente, cuando se producen booleanos se
puede optar por frases cortas con adjetivos. Por ejemplo: is_valid(...),
is_email(...), is_empty(...) etc.

El nombrado de métodos deberá iniciar con un verbo que exprese la


finalidad de la implementación:

Nombre de verbo Nombre de verbo


Descripción
(inglés) (español)
add agregar Agregar un elemento a una
colección
calculate calcular Realizar algún cálculo entre
columnas
group agrupar Agrupar varios elementos
list listar Listar datos
select seleccionar Seleccionar datos
join unir Unir datos
write escribir Escribir datos
validate validar Validar información
sort ordenar Ordenar datos
drop eliminar Eliminar datos
extract extraer Extraer información
check revisar Si se requiere comprobar
append agregar Agregar uno o más elementos
a una colección
is es Si se requiere saber es el
estado de un objeto
concat concatenar Concatenar columnas y
generar otra columna
with con Si de desea anteponer un
prefijo de condición
create crear Crear datos

e) Clases
A menos que se trate de clases estáticas, estas se utilizan para crear
objetos, por lo tanto, el nombre debe describir qué clase de objeto se está
creando. Incluso para clases estáticas, se usará como una especie de
contenedor de datos y funciones, por lo que también se debe describir su
contenido. Al igual que en el caso de las variables y propiedades, las
clases deben recibir un sustantivo como nombre.

Ejemplo: User, Producto, TransactionAccount, Pagos, etc.

f) Evitar nombres genéricos.


En la mayoría de los casos, se debe evitar colocar nombres genéricos.
Comúnmente, estos nombres pueden ser reemplazados por términos más
específicos o utilizar un nombre distinto.

Ejemplo: process() es un nombre genérico que se puede específica como


process_transaccion(). Del mismo modo, item() puede ser reemplazado
por product() para dar más detalle del elemento, según sea el contexto.
g) Mantener la consistencia.
Se debe utilizar la misma palabra con el mismo propósito para todo el
código.

Ejemplo: si se utiliza fetch_value() en una parte del código, se debe


utilizar fetch_products() y no get_products() o retrieve_products() en
otra parte del mismo código.

h) Utilizar nombres pronunciables.


Se debe utilizar nombres que sean fácil de leer, pronunciar y buscar.

Ejemplo: gen_DMYHMS no es pronunciable ni de fácil lectura. Se puede


utilizar en su lugar generate_timestamp.

i) Reemplazar los números mágicos


Cuando se requiera utilizar valores constantes, se deben definir en
variables que expresen su significado para facilitar su búsqueda y
entendimiento.

Ejemplo: MAX_WIDTH = 100

j) Evitar las codificaciones.


Se debe evitar agregar prefijos o información redundante junto con el
nombre de las variables.

Ejemplo: account_list, name_string y salary_float, se pueden


reemplazar por accounts, name y salary, respectivamente.

6.4. Reglas de funciones y métodos


a) Utilizar nombres descriptivos
Los nombres de las funciones deben ser descriptivos, por lo general
deben comenzar con un verbo en presente si estas ejecutan una acción.

Ejemplo: Para obtener los nombres de los clientes se puede crear una
función llamada get_customer_names

b) Mantener las funciones pequeñas


Mantener las funciones con un cuerpo pequeño fuerza a escribir código
más legible, debido a que se debe hacer referencias a otras funciones con
nombres descriptivos y de este modo hacer más fácil y rápida la lectura y
entendimiento del código.

A continuación, se presenta un ejemplo de una función corta que llama a


otras funciones en cada instrucción:

def login(email: str, password: str) -> None:


validate_user_input(email, password)
existing_user = find_user_by_email(email)
existing_password = validate_password(password)

c) Las funciones deben hacer una sola cosa

Las funciones o métodos deben hacer solo una cosa y hacerla bien. No
deben tener efectos secundarios, hacen lo que se espera que hagan, y
nada más. De cumplir dos tareas, se puede separar en dos métodos o
funciones.

Se debe respetar el principio de responsabilidad única (SRP, por sus


siglas en inglés) donde cada módulo, clase o función debe tener
responsabilidad sobre sólo una parte de la funcionalidad del programa.

d) No usar argumentos bandera


El argumento bandera es un tipo de argumento que indica a la función la
operación que va a realizar dependiendo de su valor, se deben evitar ya
que tienden a ser “code smell” y se considera deuda técnica. Este tipo de
argumentos booleanos declaran que la función puede hacer más de una
cosa. Puede ser confuso en la implementación y es mejor utilizar otros
mecanismos en lugar de argumentos booleanos.

Ejemplo: Se utiliza un argumento bandera is_premium para determinar la


operación que ejecutará el método:

class Concert:
def book(self, is_premium: bool) -> None:
if is_premium:
pass
# continue code
En lugar de usar el argumento bandera, es preferible separar el método
anterior en dos métodos:

class Concert:
def book_regular_book(customer) -> None:
pass
# continue code

def book_premium_book(customer) -> None:


pass
# continue code

6.5. Reglas de comentarios


a) Evitar la redundancia en los comentarios
Si el código está escrito de manera clara (se utiliza nombres apropiados,
por ejemplo), no son necesarios comentarios que expliquen líneas o
segmentos de código. En ese caso, los comentarios explicativos son
redundantes y dificultan la lectura fluida del código en lugar de facilitarla.
Ejemplo:

def create_user()-> None: #creating a new user

El comentario utilizado en el ejemplo no agrega información que se puede


entender inmediatamente con una función nombrada apropiadamente.

b) Si el código es complejo agregar ejemplos de uso


En algunos casos es necesario añadir comentarios explicativos aun
cuando se ha nombrado apropiadamente. Por ejemplo:

# Mínimo 8 caracteres, por lo menos: una letra, un número y un carácter especial


password_regex = r"/^(?=.*[A-Za-z])(?=-*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/"

c) Agregar advertencias
En algunos casos puede tener sentido agregar advertencias en el código,
por ejemplo:
def setup() -> None:
"""
:WARNING La clase HiveStorage cambiará por HadoopStorage
"""
HiveStorage().setItem('path')

d) Los argumentos de los métodos deben describirse

def setup(path: str) -> None:


"""
:param path El nombre de la ruta para almacenar
"""
HiveStorage().setItem(path)

e) La documentación de código está permitida en español o en inglés


7. Devops

FLUJO NUEVO DE INTEGRACIÓN CONTINUA PARA PROYECTOS PYSPARK

También podría gustarte