Categorías
Python

La comparación de línea de comandos de Python al analizar Bibliotecas – argparse, Docopt y haga clic

 

Tabla de Contenidos

  • inicio rápido
  • primera tarea – La StructureHandling múltiple esqueletos
  • manejo de múltiples esqueletos
  • segunda tarea – Configuración
  • tercera tarea – Bower
  • cuarto de tareas – virtualenv
  • quinto Tarea – Git Init
  • Suma y ConfirmSummaryRefactorConfirm
  • Resumen
  • Refactor
  • Confirmar
  • Run!
  • Conclusión
  • Manejo de acumulación múltiple esqueletos
  • Resumen
  • Refactor
  • Confirmar

Vamos a una utilidad de línea de comandos para generar rápidamente una estructura repetitivo frasco.

siguiendo el modelo del proyecto Frasco-esquelética, esta herramienta permitirá automatizar una serie de tareas repetitivas para que pueda obtener rápidamente un proyecto frasco en funcionamiento con la estructura, extensiones y configuraciones que prefiera , paso a paso:

una vez hecho esto, tendrá un poderoso script de andamios que puede (y debe) personalizar para satisfacer sus propias necesidades de desarrollo.

Actualizaciones :

  • 08/01/2016: actualizado a la versión 3.5.1 de Python.

inicio rápido

Para empezar, necesitamos una aplicación básica frasco. Para simplificar, vamos a utilizar la estructura real del pitón repetitivo Frasco, por lo que sólo clonarlo hacia abajo para fijar la estructura de base:

$ mkdir flask-scaffold
$ cd flask-scaffold
$ git clone https://github.com/realpython/flask-skeleton skeleton
$ rm -rf skeleton/.git
$ rm skeleton/.gitignore
$ mkdir templates
$ pyvenv-3.5 env
$ source env/bin/activate

En este artículo se utiliza Python 3.5; Sin embargo, el guión final es compatible tanto con Python 2 y 3.

primera tarea – La Estructura

guardar un nuevo archivo Python como flask_skeleton.py en el directorio raíz. Este archivo se utiliza para alimentar a toda la utilidad de andamios. Abrirlo en ti editor de texto favorito y añada el siguiente código:

# -*- coding: utf-8 -*-

import sys
import os
import argparse
import shutil

# Globals #

cwd = os.getcwd()
script_dir = os.path.dirname(os.path.realpath(__file__))

def main(argv):

# Arguments #

parser = argparse.ArgumentParser(description='Scaffold a Flask Skeleton.')
parser.add_argument('appname', help='The application name')
parser.add_argument('-s', '--skeleton', help='The skeleton folder to use.')
args = parser.parse_args()

# Variables #

appname = args.appname
fullpath = os.path.join(cwd, appname)
skeleton_dir = args.skeleton

# Tasks #

# Copy files and folders
shutil.copytree(os.path.join(script_dir, skeleton_dir), fullpath)

if __name__ == '__main__':
main(sys.argv)

Aquí, estamos usando argparse para obtener una nombreaplic para el nuevo proyecto y luego copiar el directorio esqueleto (a través de shutil), que contiene el texto modelo de proyecto , para volver a crear rápidamente la estructura del proyecto.

El shutil.copytree () método (fuente) se utiliza para copiar recursivamente un directorio de origen a un directorio de destino (siempre y cuando el directorio de destino no existe).

probarlo:

$ python flask_skeleton.py new_project -s skeleton

Esto debería hacer una copia de la estructura real del pitón repetitivo Frasco (fuente) a un nuevo directorio llamado “nuevo_proyecto” (destino). ¿Funcionó? Si es así, retire el nuevo proyecto, ya que todavía hay mucho trabajo por hacer:

$ rm -rf new_project

manejo de múltiples esqueletos

¿Y si necesita una aplicación con una base de datos MongoDB o un plan de pagos? Todas las aplicaciones tienen necesidades específicas y que, evidentemente, no pueden crear un esqueleto para todos ellos, pero quizá hay ciertas características que se necesitan la mayoría de las veces. Tal vez necesita una base de datos NoSQL un cincuenta por ciento de las veces, por ejemplo. Puede añadir un nuevo esqueleto a la raíz para lograr esto. Luego, cuando se ejecuta el comando andamio, sólo tiene que especificar el nombre del directorio que contiene el esqueleto aplicación que desea hacer una copia de.

segunda tarea – Configuración

Ahora tenemos que generar un archivo config.py a medida para cada esqueleto. Este script va a hacer eso por nosotros; dejar que el código haga el trabajo repetitivo! En primer lugar, añadir un archivo llamado config.jinja2 en la carpeta de plantillas :

# config.jinja2

import os
basedir = os.path.abspath(os.path.dirname(__file__))

class BaseConfig(object):
"""Base configuration."""
SECRET_KEY = '{{ secret_key }}'
DEBUG = False
BCRYPT_LOG_ROUNDS = 13
WTF_CSRF_ENABLED = True
DEBUG_TB_ENABLED = False
DEBUG_TB_INTERCEPT_REDIRECTS = False

class DevelopmentConfig(BaseConfig):
"""Development configuration."""
DEBUG = True
BCRYPT_LOG_ROUNDS = 13
WTF_CSRF_ENABLED = False
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'dev.sqlite')
DEBUG_TB_ENABLED = True

class TestingConfig(BaseConfig):
"""Testing configuration."""
DEBUG = True
TESTING = True
BCRYPT_LOG_ROUNDS = 13
WTF_CSRF_ENABLED = False
SQLALCHEMY_DATABASE_URI = 'sqlite:///'
DEBUG_TB_ENABLED = False

class ProductionConfig(BaseConfig):
"""Production configuration."""
SECRET_KEY = '{{ secret_key }}'
DEBUG = False
SQLALCHEMY_DATABASE_URI = 'postgresql://localhost/example'
DEBUG_TB_ENABLED = False

Al comienzo de la secuencia de comandos de andamio, flask_skeleton.py, justo antes de la función main (), necesitamos inicializar Jinja2 a fin de hacer la configuración correctamente.

# Jinja2 environment
template_loader = jinja2.FileSystemLoader(searchpath=os.path.join(script_dir, "templates"))
template_env = jinja2.Environment(loader=template_loader)

Asegúrese de añadir la importación, así:

import jinja2

Instalar: volver

$ pip install jinja2
$ pip freeze > requirements.txt

Mirando a la plantilla, config.jinja2 , tenemos una variable que debe ser definido – {{}} secret_key que hacer. esto, podemos utilizar el módulo de códecs.

Para las importaciones de complemento flask_skeleton.py:

import codecs

Agregue el código siguiente al final de la función main ():

# Create config.py
secret_key = codecs.encode(os.urandom(32), 'hex').decode('utf-8')
template = template_env.get_template('config.jinja2')
template_var = {
'secret_key': secret_key,
}
with open(os.path.join(fullpath, 'project', 'config.py'), 'w') as fd:
fd.write(template.render(template_var))

¿Qué pasa si usted maneja varios esqueletos y necesita varias plantillas de configuración?

sencillo: sólo tiene que comprobar lo que el esqueleto se pasa como argumento y utilice la plantilla de configuración apropiada. Tenga en cuenta que os.path.join (ruta completa, ‘proyecto’, ‘config.py’) debe representar la ruta donde la configuración se debe almacenar en su esqueleto. Si esto es diferente para cada esqueleto, entonces usted debe especificar la carpeta en la que el archivo de configuración se almacena en argparse como argumento adicional.

listo para la prueba?

$ python flask_skeleton.py new_project -s skeleton

Asegúrese de que el archivo config.py está presente en la carpeta “nuevo_proyecto / proyecto” y retire el nuevo proyecto: rm-rf nuevo_proyecto

tercera tarea – Bower

Así es: Vamos a utilizar Bower para descargar y gestionar las bibliotecas estáticas. Para añadir glorieta apoyo a la secuencia de comandos del andamio, se inicia con la adición de otro argumento:

parser.add_argument('-b', '--bower', help='Install dependencies via bower')

Y para manejar el funcionamiento de la glorieta, añada el siguiente código justo debajo de la sección de configuración de la secuencia de comandos de andamio:

# Add bower dependencies
if args.bower:
bower = args.bower.split(',')
bower_exe = which('bower')
if bower_exe:
os.chdir(os.path.join(fullpath, 'project', 'client', 'static'))
for dependency in bower:
output, error = subprocess.Popen(
[bower_exe, 'install', dependency],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
).communicate()
# print(output)
if error:
print("An error occurred with Bower")
print(error)
else:
print("Could not find bower. Ignoring.")

No se olvide de añadir el módulo de subproceso a la sección de importación de flask_skeleton.py – subproceso de importación.

¿Se notaron los método que () (fuente)? Esto esencialmente utiliza el Unix / Linux que la herramienta con el fin de indicar dónde un ejecutable está instalado en el sistema de archivos. Por lo tanto, en el código anterior, estamos comprobando para ver si se ha instalado la glorieta. Si tienes curiosidad por saber cómo esto funciona, prueba a cabo en el intérprete de Python 3:

>>> import shutil
>>> shutil.which('bower')
'/usr/local/bin/bower'

Desafortunadamente, este método, que (), es nuevo en Python 3.3, así que, si estás usando Python 2, entonces necesidad de instalar un paquete separado – shutilwhich:

$ pip install shutilwhich
$ pip freeze > requirements.txt

actualización de las importaciones:

if sys.version_info < (3, 0): from shutilwhich import which else: from shutil import which

por último, te convertirá atención a las siguientes líneas de código:

output, error = subprocess.Popen(
[bower_exe, 'install', dependency],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
).communicate()
# print(output)
if error:
print("An error occurred with Bower")
print(error)

Echen un vistazo a la documentación oficial subproceso. En pocas palabras, se utiliza para invocar comandos shell externos. En el código anterior estamos capturando simplemente la salida de stdout y stderr.

Si tienes curiosidad de ver lo que la salida es, elimine la sentencia print, # de impresión (salida), y luego ejecutar el código ...

Antes de probar, este código asume que en su carpeta de esqueleto (s), usted tiene una carpeta “proyecto” que contiene una carpeta “estática”. Frasco aplicación clásica. En la línea de comandos, ahora se puede instalar varias dependencias de esta manera:

$ python flask_skeleton.py new_project -s skeleton -b 'angular, jquery, bootstrap'

cuarto de tareas - virtualenv

Desde el entorno virtual es una de las partes importantes la mayoría de cualquier frasco de aplicaciones (err, Python), creando el virtualenv usando el andamio guión va a ser muy útil. Como de costumbre, empezar añadiendo el argumento:

parser.add_argument('-v', '--virtualenv', action='store_true')

Y a continuación, añadir el siguiente código debajo de la sección Bower:

# Add a virtualenv
virtualenv = args.virtualenv
if virtualenv:
virtualenv_exe = which('pyvenv')
if virtualenv_exe:
output, error = subprocess.Popen(
[virtualenv_exe, os.path.join(fullpath, 'env')],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
).communicate()
if error:
with open('virtualenv_error.log', 'w') as fd:
fd.write(error.decode('utf-8'))
print("An error occurred with virtualenv")
sys.exit(2)
venv_bin = os.path.join(fullpath, 'env/bin')
output, error = subprocess.Popen(
[
os.path.join(venv_bin, 'pip'),
'install',
'-r',
os.path.join(fullpath, 'requirements.txt')
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
).communicate()
if error:
with open('pip_error.log', 'w') as fd:
fd.write(error.decode('utf-8'))
sys.exit(2)
else:
print("Could not find virtualenv executable. Ignoring")

Este fragmento se supone que hay un archivo requirements.txt en la carpeta de “esqueleto” en el directorio raíz. Si es así, se creará un virtualenv y luego instalar las dependencias.

quinto Tarea - Git Init

notar un patrón todavía? Añadir el argumento:

parser.add_argument('-g', '--git', action='store_true')

continuación, agregue el código en la tarea para la virtualenv:

# Git init
if args.git:
output, error = subprocess.Popen(
['git', 'init', fullpath],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
).communicate()
if error:
with open('git_error.log', 'w') as fd:
fd.write(error.decode('utf-8'))
print("Error with git init")
sys.exit(2)
shutil.copyfile(
os.path.join(script_dir, 'templates', '.gitignore'),
os.path.join(fullpath, '.gitignore')
)

Ahora dentro de las plantillas de la carpeta añaden un .gitignore archivo, y luego añadir los archivos y carpetas que desea ignorar. Coge el ejemplo de Github, si es necesario. Prueba de nuevo.

Sum y Confirmar

Por último, vamos a añadir un resumen agradable antes de crear la aplicación y luego pedir la confirmación del usuario antes de ejecutar la secuencia de comandos ...

Resumen

Añadir un archivo llamado brief.jinja2 a las “plantillas” carpeta :

Welcome! The following settings will be used to create your application:

Python Version: {{ pyversion }}
Project Name: {{ appname }}
Project Path: {{ path }}
Virtualenv: {% if virtualenv %}Enabled{% else %}Disabled{% endif %}
Skeleton: {{ skeleton }}
Git: {% if git %}Yes{% else %}{{ disabled }}No{% endif %}
Bower: {% if bower %}Enabled{% else %}Disabled{% endif %}
{% if bower %}Bower Dependencies: {% for dependency in bower %}{{ dependency }}{% endfor %}{% endif %}

Ahora sólo tenemos que coger todos los argumentos suministrados por el usuario y luego hacer la plantilla. En primer lugar, añadir la importación - plataforma de importación - a la sección de importación y luego el siguiente código justo debajo de la sección “Variables” en el flask_skeleton.py guión:

# Summary #

def generate_brief(template_var):
template = template_env.get_template('brief.jinja2')
return template.render(template_var)

template_var = {
'pyversion': platform.python_version(),
'appname': appname,
'bower': args.bower,
'virtualenv': args.virtualenv,
'skeleton': args.skeleton,
'path': fullpath,
'git': args.git
}

print(generate_brief(template_var))

Prueba esto:

$ python flask_skeleton.py new_project -s skeleton -b 'angular, jquery, bootstrap' -g -v

Debería ver algo como:

Welcome! The following settings will be used to create your application:

Python Version: 3.5.1
Project Name: new_project
Project Path: /Users/michael/repos/realpython/flask-scaffold
ew_project
Virtualenv: Enabled
Skeleton: skeleton
Git: Yes
Bower: Enabled
Bower Dependencies: angular, jquery, bootstrap

Nice!

Refactor

Ahora tenemos que refactorizar el guión un poco para comprobar si hay errores en primer lugar. Sugiero agarrar el código de la etiqueta de refactorizar y luego comparar el diff, ya que hay una serie de pequeños cambios.

Asegúrese de que se utiliza la secuencia de comandos actualizada de la etiqueta refactor antes de continuar.

Confirmar

Ahora vamos a añadir la funcionalidad de la confirmación del usuario mediante la actualización si __name__ == '__main__' ::

if __name__ == '__main__':
arguments = get_arguments(sys.argv)
print(generate_brief(arguments))
if sys.version_info < (3, 0): input = raw_input proceed = input("\nProceed (yes
o)? ")
valid = ["yes", "y", "no", "n"]
while True:
if proceed.lower() in valid:
if proceed.lower() == "yes" or proceed.lower() == "y":
main(arguments)
print("Done!")
break
else:
print("Goodbye!")
break
else:
print("Please respond with 'yes' or 'no' (or 'y' or 'n').")
proceed = input("\nProceed (yes
o)? ")

Esto debería ser bastante sencillo.

Run!

Si utiliza Linux o Mac con el que puede hacer este script más fácil de ejecutar. Basta con añadir el siguiente alias a cualquiera Bashrc o .zshrc , la personalización para que coincida con la estructura de directorio:

alias flaskcli="python /Users/michael/repos/realpython/flask-scaffold/flask_skeleton.py"

NOTA : Si tiene tanto Python 2.7 y Python 3.5 instalado, tendrá que especificar la versión que querer usar - ya sea pitón o python3.

Retire el nuevo proyecto (si es necesario) - rm-rf nuevo_proyecto - y luego probar el guión una vez más para confirmar:

$ flaskcli new_project -s skeleton -b 'angular, jquery, bootstrap' -g -v

Conclusión

¿Qué opinas? ¿Nos hemos perdido algo? ¿Qué otros argumentos añadirías a argparse el fin de personalizar su script andamio aún más? ¡Comenta abajo!

Coge el código final de la cesión temporal.

Esta es una pieza colaboración entre Depado y la gente en el Real Python. Ediciones hechas por Derrick Kearney.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *