diff --git a/README.md b/README.md index e69de29..8b7bc88 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,16 @@ +## [Máster en Ingeniería Web por la Universidad Politécnica de Madrid (miw-upm)](http://miw.etsisi.upm.es) +## Back-end con Tecnologías de Código Abierto (BETCA). +> Este proyecto es un apoyo docente de la asignatura y contiene ejemplos prácticos sobre Python + +### Tecnologías necesarias +`Python` `GitHub` + +### :gear: Instalación del proyecto +1. Clonar el repositorio en tu equipo, **mediante consola**: +```sh +> cd +> git clone https://github.com/miw-upm/betca-python +``` +2. Importar el proyecto mediante **PyCharm** + 1. **Open**, y seleccionar la carpeta del proyecto. + 1. Instalar las dependencias diff --git a/config.env b/config.env new file mode 100644 index 0000000..2ea88d8 --- /dev/null +++ b/config.env @@ -0,0 +1,7 @@ +ENVIRONMENT=prod + +DEV_VAR=value-on-dev +DEV_JWT_SECRET=secret-to-test + +PROD_VAR=value-on-production +PROD_JWT_SECRET=secret-${USERNAME} \ No newline at end of file diff --git a/language/1_hello_world.py b/language/1_hello_world.py new file mode 100644 index 0000000..086dfb1 --- /dev/null +++ b/language/1_hello_world.py @@ -0,0 +1,5 @@ +# Hello World +""" +Hello World!!! +""" +print('Hello world!') diff --git a/language/variables.py b/language/2_variables.py similarity index 56% rename from language/variables.py rename to language/2_variables.py index 961f12f..9a1a275 100644 --- a/language/variables.py +++ b/language/2_variables.py @@ -1,24 +1,29 @@ -import datetime +from datetime import date # types: str, int, float, complex, bool, list, tuple, range, dict, set, frozenset, bytes, bytearray, memoryview -str_var: str # STATIC var is not defined, it is not possible to use until creating with = -str_var = 'string' # DYNAMIC. str_var is created. It's possible "string", esc==\ -var = 5 # var is created +str_var: str # STATIC var is not defined, it is not possible to use until creating with '=' +# print(str_var) ERROR!!! +str_var = 'string' # DYNAMIC. str_var is created. It is possible use '...' or "...", esc==\ + +var = 5 # var is created, it is var = int(5) null_var = None # None is null and var is created casting_var = str(var) # casting: str(), float()... i = 3 + 2j # complex: i.real, i.imag ... -var = 'other' # dynamic type +var = 'other' # dynamic type: from int to str + # One line: one sentence. With \ the line continue large_str = '''line one\ line one & end of line other line''' # '\' without end of line -print(large_str[-10:-5]) # Use negative indexes to start from the end of the string -print(large_str[0:5]) -print(large_str[5:]) +print('1- ', large_str[-10:-5]) # Use negative indexes to start from the end of the string +print('2- ', large_str[0:5]) +print('3- ', large_str[5:]) +print('4- ', large_str[:]) # it is copy age = 36 -anniversary = datetime.date(1991, 10, 12) -txt = f"My name is \"John\", and I am {age}, my anniversary is {anniversary:%A, %B %d, %Y}" -print('text {}'.format(age)) +anniversary = date(1991, 10, 12) +txt = f"My name is \"John\", and I am {age}, my anniversary is {anniversary:%A, %B %d, %Y}" # \ is esc +print('5- ', 'text {}'.format(age)) +print('6- ', f'text {age}') my_list = ["apple", "banana", "cherry"] # list .append .extend .remove .pop .popleft .clear .reverse ... my_tuple = "apple", "banana", "cherry" # tuple immutable @@ -29,3 +34,8 @@ my_set_immutable = frozenset({"apple", "banana", "cherry"}) # set immutable bytes_var = b'byte' + +print('7- type of str_var: ', type(str_var)) +print('8- type of age: ', type(age)) +print('9- type of anniversary: ', type(anniversary)) +print('10- type of bytes_var: ', type(bytes_var)) diff --git a/language/3_operators.py b/language/3_operators.py new file mode 100644 index 0000000..25d78eb --- /dev/null +++ b/language/3_operators.py @@ -0,0 +1,15 @@ +# Arithmetic operators: +, -, *, /, //, %, ** +var = 5 +print('1- ', '5/2 = ', var / 2) +print('2- ', '5//2 = ', var // 2) +print('3- ', '5%2 = ', var % 2) +print('4- ', '5**2 = ', var ** 2) + +# Assignment operators: =, +=, -=, *=, /=, %=, //=, **=, &=, |=, ^=, >>=, <<= +var *= 3 +print('5- ', 'var *=3 ', var) + +# Logical operators: and, or, not, ==, !=, >, <, >=, <=, is is not +print('6- ', '17>15>12 = ', 17 > var > 12) + +# Bitwise Operators: &, |, ^, ~, <<, >> diff --git a/language/flows.py b/language/4_flows.py similarity index 54% rename from language/flows.py rename to language/4_flows.py index 34acb85..a0363bc 100644 --- a/language/flows.py +++ b/language/4_flows.py @@ -1,20 +1,20 @@ -# Flows g = 1 if g < 0: g = 0 - print('Negative changed to zero') + print('1- ', 'Negative changed to zero') elif g == 0: - print('Zero') + print('1- ', 'Zero') else: - print('More') + print('1- ', 'More') # Loop: break & continue & else words = ['cat', 'window', 'defenestrate'] for w in words: - print(w) + print('2- ', w) for i in range(0, 10, 2): - print(i) + print('3- ', i) while g < 10: + print('4- ', g) g += 1 diff --git a/language/5_dicts.py b/language/5_dicts.py new file mode 100644 index 0000000..a9fd731 --- /dev/null +++ b/language/5_dicts.py @@ -0,0 +1,14 @@ +# dict: len(my_dict), my_dict['name'] or my_dict.get("name"), .keys(), .values(), .clear(), .copy() +my_dict = {"name": "John", "age": 36, "dni": 12345678, "role": None} +print('1- ', my_dict.keys()) +print('1- ', my_dict.values()) + +for key in my_dict.keys(): + print('2- ', key, ':', my_dict[key]) + print('2- ', key, ':', my_dict.get(key)) + +print('3- ', my_dict['age']) + +my_dict.setdefault('name', 'only-if-not-exist') +my_dict.setdefault('new', 'new-value') +print('4- ', my_dict.keys(), '::', my_dict.values()) diff --git a/language/6_functions.py b/language/6_functions.py new file mode 100644 index 0000000..08b2c52 --- /dev/null +++ b/language/6_functions.py @@ -0,0 +1,55 @@ +def cheese_shop(kind, *arguments, **keywords) -> int: # position, *: optional, **: dict + print('1- ', "-- Do you have any", kind, "?") + for arg in arguments: + print('1- ', arg) + for key in keywords: # .keys() is optional, + print('1- ', key, ":", keywords[key]) + return 10 + + +print('0- type of def: ', type(cheese_shop)) + +g = cheese_shop("-Position-", + "Optional-1", "Optional-2", + key1="Michael Palin", key2="John Cleese", key3="Cheese Shop Sketch") +print("1- ", 'cheese-shop return: ', g) + + +def model(index, **keywords): # keywords: Dict[str,Any] + print(index, 'keywords: ', keywords) + for key in keywords: # keywords.keys() is optional, + print(index, key, ":", keywords[key]) + + +my_dict = {"name": "John", "age": 36, "dni": 12345678} +model('2- ', **my_dict) # convert key-value pairs into arguments +# model(my_dict) ERROR!!! +model('3- ', one=my_dict) + + +def make_increment(n): + return lambda param: param + n + + +f = make_increment(665) +print('4- ', 'lambda: ', f(1)) + +# variable scope +global_var = 'global' + + +def fun0(): + non_local_var = 'nonlocal' + + def fun1(): + global global_var # for updating + nonlocal non_local_var # for updating + local_var = 'local' + non_local_var = 'from f1' + global_var = 'from f1' + print('6- ', 'fun1 >> global_var:', global_var, ', non_local_var:', non_local_var, ', local_var:', local_var) + + fun1() + + +fun0() diff --git a/language/7_classes.py b/language/7_classes.py new file mode 100644 index 0000000..7ff478d --- /dev/null +++ b/language/7_classes.py @@ -0,0 +1,98 @@ +# __***__ are especial functions and attributes + + +def dynamic_function(): + print('dynamic_function') + + +class MyClass: + """A simple example class""" # default: __doc__=... + i: int # it is a instance attribute + att = 'static' # it is a static attribute + + def __init__(self, name): # constructor: __init__ + print('Init MyClass:', name) + self.i = len(name) + self.name = name # 'other' is a instance attribute + self.dynamic = dynamic_function + + def public(self): # public + return 'hello world (' + str(self.i) + ')' + + def __private(self): # private + pass + + +print('0- type of class: ', type(MyClass)) + +print('1 ---------------- ') +my_class = MyClass('me') +my_class.dynamic() +my_class.new_attribute = "New!!!" +my_class.new_function = dynamic_function +print(my_class.new_attribute) + + +# Inheritance +class ChildClass(MyClass): + def __init__(self): + super().__init__('child') + + +print('2 ---------------- ') +ChildClass() + + +class Two: + two: str + + def __init__(self): + print('Init Two') + + +# Multiple inheritance +class Multiple(MyClass, Two): + value: str + + +print('3 ---------------- ') +multiple = Multiple('multiple') +multiple.i = 3 +multiple.name = "name" +multiple.two = "2" +multiple.value = "value" + + +class WithDecorator: + + def __init__(self, first_name, last_name): + self.first_name = first_name + self.last_name = last_name + + @classmethod + def from_string(cls, name_str): + first_name, last_name = map(str, name_str.split(' ')) + return cls(first_name, last_name) + + @staticmethod + def is_full_name(name_str): + names = name_str.split(' ') + return len(names) > 1 + + +# __doc__ __init__ __name__ __module__ __call__ ... + +print('4 ---------------- ') +print('__doc__ ', MyClass.__doc__) +print('__name__ ', MyClass.__name__) +print('__module__ ', MyClass.__module__) + + +class Callable: + def __call__(self, value): # class default function + print('__call__ >> value: ', value) + + +my_callable = Callable() +my_callable("!!!") # class is callable +# my_class() ERROR!!! diff --git a/language/classes.py b/language/classes.py deleted file mode 100644 index 33b4daf..0000000 --- a/language/classes.py +++ /dev/null @@ -1,76 +0,0 @@ -print(f"File one __name__ is set to: {__name__}") - - -class MyClass: - """A simple example class""" # default: __doc__=... - i: int # it is a instance - att = 'static' # it is a static - - def __init__(self, name): # constructor - print('MyClass init') - self.i = len(name) - self.other = name # 'other' is a instance - - def f(self): # public - return 'hello world (' + str(self.i) + ')' - - def __private(self): # private - pass - - -class ChildClass(MyClass): - - def __init__(self): - super().__init__('child') - - -class DynamicClass: - pass - - -def fun(): - print('dynamic fun...') - - -class Dto: - name: str - mobile: int - - -class WithDecorator: - - def __init__(self, first_name, last_name): - self.first_name = first_name - self.last_name = last_name - - @classmethod - def from_string(cls, name_str): - first_name, last_name = map(str, name_str.split(' ')) - student = cls(first_name, last_name) - return student - - @staticmethod - def is_full_name(name_str): - names = name_str.split(' ') - return len(names) > 1 - - -# when the interpreter runs a module, the __name__ variable will be set as __main__, -# But if the code is importing the module from another module, then the __name__ will be set to that module’s name. -if __name__ == "__main__": - print('running main... ') - c = MyClass('one') - c3 = MyClass('three') - print('one: ', c.i, c.other, c.att) - print('three: ', c3.i, c3.other, c3.att) - print(c.f()) - print(c.__doc__) - print(MyClass.__name__) # it is 'MyClass' - print(MyClass.att, c.att) - child = ChildClass() - - # Dynamic class - dynamic = DynamicClass() - dynamic.name = 'Ops' - dynamic.fun = fun - dynamic.fun() diff --git a/language/dicts.py b/language/dicts.py deleted file mode 100644 index a6f3838..0000000 --- a/language/dicts.py +++ /dev/null @@ -1,13 +0,0 @@ -# dict: len(my_dict), my_dict['name'] or my_dict.get("name"), .keys(), .values(), .clear(), .copy() -my_dict = {"name": "John", "age": 36, "dni": 12345678} - -print(my_dict.keys()) - -for key in my_dict.keys(): - print(my_dict[key]) - print(my_dict.get(key)) - -for value in my_dict.values(): - print(value) - -print(my_dict['age']) diff --git a/language/functions.py b/language/functions.py deleted file mode 100644 index 33517c5..0000000 --- a/language/functions.py +++ /dev/null @@ -1,59 +0,0 @@ -# functions -# If / and * are not present, arguments may be passed to a function by position or by keyword. - - -def cheese_shop(kind, *arguments, **keywords) -> int: # keywords: Dict[str,Any] - print("-- Do you have any", kind, "?") - print("-- I'm sorry, we're all out of", kind) - for arg in arguments: - print(arg) - for key in keywords: # .keys() is optional, - print(key, ":", keywords[key]) - for item in keywords.items(): - print(item[0], ':', item[1]) - return 10 - - -g = cheese_shop("Limburger", "It's very runny, sir.", - "It's really very, VERY runny, sir.", - shopkeeper="Michael Palin", - client="John Cleese", - sketch="Cheese Shop Sketch") -print('cheese_shop: ', g) - - -def model(**keywords): # keywords: Dict[str,Any] - print('keywords ', keywords) - - -my_dict = {"name": "John", "age": 36, "dni": 12345678} -model(**my_dict) # convert key-value pairs into arguments - - -def make_increment(n): - return lambda param: param + n - - -f = make_increment(42) -print('lmbda: ', f(1)) - -# variable scope -glb = 'global' - - -def fun0(): - nl = 'nonlocal' - - def fun1(): - global glb # for updating - nonlocal nl # for updating - loc = 'local' - print('fun1: ', glb, ':', nl, ':', loc) - nl = 'from local' - glb = 'from local' - - fun1() - - -fun0() -print('glb: ', glb) diff --git a/language/hello_world.py b/language/hello_world.py deleted file mode 100644 index c582302..0000000 --- a/language/hello_world.py +++ /dev/null @@ -1,5 +0,0 @@ -# Hello World -""" -Hello World!!! -""" -print('Hello world!') # Print 'Hello World!' diff --git a/language/operators.py b/language/operators.py deleted file mode 100644 index 411de31..0000000 --- a/language/operators.py +++ /dev/null @@ -1,13 +0,0 @@ -# Operators -# Arithmetic operators: +, -, *, /, //, %, ** -int_var = 5 # var is created -print('5/2 = ', int_var / 2) -print('5//2 = ', int_var // 2) -print('5%2 = ', int_var % 2) -print('5**2 = ', int_var ** 2) -# Assignment operators: =, +=, -=, *=, /=, %=, //=, **=, &=, |=, ^=, >>=, <<= -int_var *= 3 -print('5*3 = ', int_var) -# Logical operators: and, or, not, ==, !=, >, <, >=, <=, is is not -print('17>15>12 = ', 17 > int_var > 12) -# Bitwise Operators: &, |, ^, ~, <<, >> diff --git a/language/snippet/__init__.py b/language/snippet/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/language/enums.py b/language/snippet/enums.py similarity index 100% rename from language/enums.py rename to language/snippet/enums.py diff --git a/language/functional.py b/language/snippet/functional.py similarity index 100% rename from language/functional.py rename to language/snippet/functional.py diff --git a/language/snippet/https.py b/language/snippet/https.py new file mode 100644 index 0000000..f08bcfe --- /dev/null +++ b/language/snippet/https.py @@ -0,0 +1,9 @@ +from http import HTTPStatus +from http.client import responses + +print(HTTPStatus.NOT_FOUND.value) +print(HTTPStatus.NOT_FOUND.phrase) + +print(responses[404]) + + diff --git a/language/snippet/modules.py b/language/snippet/modules.py new file mode 100644 index 0000000..d0b963a --- /dev/null +++ b/language/snippet/modules.py @@ -0,0 +1,6 @@ +# when the interpreter runs a module, the __name__ variable will be set as __main__, +# But if the code is importing the module from another module, then the __name__ will be set to that module’s name. +# Execute only if run as a script +if __name__ == "__main__": + print('running main... ') + diff --git a/language/regular_expresion.py b/language/snippet/regular_expresion.py similarity index 100% rename from language/regular_expresion.py rename to language/snippet/regular_expresion.py diff --git a/language/snippet/various.py b/language/snippet/various.py new file mode 100644 index 0000000..50b1980 --- /dev/null +++ b/language/snippet/various.py @@ -0,0 +1,29 @@ +def fun(): + pass + + +class Clazz: + def show(self): + print(self, 'show') + + +print(type(fun)) +print(type(Clazz)) + +var = 23 +var_2 = 23.4 +var_3 = fun +var_4: float +var_5: fun = fun +var_5() +var_6: Clazz = Clazz() +var_6.show() + + +print(type(var)) +print(type(var_2)) +print(type(var_3)) +# error: print(type(var4)) # var4 is undefined +print(type(var_5)) +print(type(var_6)) + diff --git a/requirements.txt b/requirements.txt index 7f452aa..347ad50 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,5 @@ mongoengine~=0.22.1 bcrypt~=3.2.0 pyjwt~=2.0.0 requests~=2.25.1 +python-dotenv~=0.15.0 + diff --git a/rest/auth_resource.py b/rest/auth_resource.py index bdd84a9..80a23e8 100644 --- a/rest/auth_resource.py +++ b/rest/auth_resource.py @@ -16,14 +16,12 @@ def read_with_basic_beginning(credentials: HTTPBasicCredentials = Depends(HTTPBa return {"username": credentials.username, "password": credentials.password} -basic_security = HTTPBasic() - - -def basic_authentication(credentials: HTTPBasicCredentials = Depends(basic_security)) -> str: +def basic_authentication(credentials: HTTPBasicCredentials = Depends(HTTPBasic())) -> str: correct_username = secrets.compare_digest("jes", credentials.username) # reduce the risk of timing attacks - print('token:', secrets.token_urlsafe(16)) # secrets.token_hex(16) bd_hashed_password = bcrypt.hashpw(b"pass", bcrypt.gensalt()) correct_password = bcrypt.checkpw(credentials.password.encode('utf-8'), bd_hashed_password) + print('token:', secrets.token_urlsafe(16)) # secrets.token_hex(16) + print('pass:', bd_hashed_password) # secrets.token_hex(16) if not (correct_username and correct_password): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, @@ -44,10 +42,7 @@ def create_jwt_token(username: str = Depends(basic_authentication)): return {"token": encoded_jwt} -bearer_security = HTTPBearer() - - -def bearer_authentication(credentials: HTTPAuthorizationCredentials = Depends(bearer_security)) -> str: +def bearer_authentication(credentials: HTTPAuthorizationCredentials = Depends(HTTPBearer())) -> str: try: payload = jwt.decode(credentials.credentials, 'secret_key-Y01oEAl3iz', algorithms=["HS512", "HS256"]) username: str = payload.get("sub") diff --git a/rest/basic_resource.py b/rest/basic_resource.py index 5a04f24..ab152f8 100644 --- a/rest/basic_resource.py +++ b/rest/basic_resource.py @@ -4,7 +4,7 @@ basic = APIRouter( prefix="/basic", - tags=["basic"], + tags=["basic"], # OpenAPI ) @@ -19,7 +19,7 @@ def create(dto: Dto) -> Dto: @basic.get("/{ide}") -def read(ide: str): +def read(ide: str) -> Dto: return Dto(id=ide, name="dto", description='desc') @@ -32,4 +32,3 @@ def update(ide: str, dto: Dto) -> Dto: @basic.delete("/{ide}") def delete(ide: str): print('Dto(', ide, ') deleted') - return None diff --git a/rest/main.py b/rest/main.py index 11cf6e1..4065fb8 100644 --- a/rest/main.py +++ b/rest/main.py @@ -8,3 +8,4 @@ app.include_router(basic) app.include_router(validation) app.include_router(auth) + diff --git a/settings/__init__.py b/settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/settings/config.py b/settings/config.py new file mode 100644 index 0000000..e150451 --- /dev/null +++ b/settings/config.py @@ -0,0 +1,36 @@ +from typing import Optional + +from pydantic import BaseSettings, Field + + +class Settings(BaseSettings): + ENVIRONMENT: str = Field(None, env="ENVIRONMENT") # por default, 'env' is a environment var + VAR: Optional[str] + JWT_SECRET: Optional[str] + + class Config: + env_file: str = "../config.env" + + +class DevSettings(Settings): + class Config: + env_prefix: str = "DEV_" + + +class ProdSettings(Settings): + class Config: + env_prefix: str = "PROD_" + + +def get_settings(): + if "prod" == Settings().ENVIRONMENT: + return ProdSettings() + else: + return DevSettings() + + +config = get_settings() + +print(config.ENVIRONMENT) +print(config.VAR) +print(config.JWT_SECRET) diff --git a/tests/actual.py b/tests/actual.py index daa53a3..65a5091 100644 --- a/tests/actual.py +++ b/tests/actual.py @@ -1,3 +1,4 @@ + def method(arg): print('>>> method... return:', arg) return arg diff --git a/tests/test_rest.py b/tests/test_rest.py index b5bed93..a027c43 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -1,4 +1,3 @@ -import unittest from http import HTTPStatus from unittest import TestCase @@ -14,7 +13,9 @@ class TestService(TestCase): def test_read_basic(self): response = client.get("/basic/666") self.assertEqual(HTTPStatus.OK, response.status_code) - print(response.json()) + self.assertEqual('666', response.json()['id']) + self.assertEqual({'id': '666', 'name': 'dto', 'description': 'desc', 'value': None}, response.json()) + print('response: ', response.json()) def test_create(self): response = client.post("/basic", json=Dto(id='1', name="dto", description='desc').dict()) @@ -22,7 +23,7 @@ def test_create(self): print(response.json()) def test_update(self): - response = client.put("/basic/666", json=Dto(id='1', name="dto", description='desc').dict()) + response = client.put("/basic/666", json={'id': '666', 'name': 'dto', 'description': 'desc'}) self.assertEqual(HTTPStatus.OK, response.status_code) print(response.json()) @@ -47,10 +48,9 @@ def test_read_auth_error(self): def test_create_token_and_get(self): response = client.post("/auth/bearer", auth=('jes', 'pass')) self.assertEqual(HTTPStatus.OK, response.status_code) - token = dict(response.json())['token'] + token = response.json()['token'] print(token) - bearer = "Bearer " + token - response = client.get("/auth/bearer", headers={"Authorization": bearer}) + response = client.get("/auth/bearer", headers={"Authorization": "Bearer " + token}) self.assertEqual(HTTPStatus.OK, response.status_code) print(response.json()) diff --git a/tests/test_unittest.py b/tests/test_unittest.py index f2bc1d7..4e242e1 100644 --- a/tests/test_unittest.py +++ b/tests/test_unittest.py @@ -64,7 +64,3 @@ def test_3(self): self.assertLessEqual(2, 2) self.assertRegex("abbb", r"ab*") self.assertNotRegex("bbb", r"ab*") - - - -