Kodduu Python
1.02K subscribers
319 photos
29 videos
212 links
Научись программировать на Python на интересных примерах

Самый быстрый курс https://stepik.org/a/187914
Самый нескучный курс https://stepik.org/a/185238

Во вопросам сотрудничества: @AlexErf
Download Telegram
🔒 Декораторы для улучшения безопасности API


from functools import wraps

def require_authentication(func):
@wraps(func)
def wrapper(*args, **kwargs):
user_authenticated = kwargs.get('authenticated', False)
if not user_authenticated:
raise PermissionError("Требуется аутентификация для доступа к этой функции.")
return func(*args, **kwargs)
return wrapper

# Пример использования
@require_authentication
def get_user_data(user_id: int, authenticated=False) -> dict:
# Имитация получения данных пользователя
return {"user_id": user_id, "name": "Alice"}

try:
print(get_user_data(user_id=1, authenticated=True)) # Успешный доступ
print(get_user_data(user_id=2, authenticated=False)) # Ошибка доступа
except PermissionError as e:
print(e)


📌 Этот декоратор добавляет проверку аутентификации для функции, что полезно для защиты API от неавторизованного доступа. Он оборачивает функцию, проверяет наличие флага аутентификации и выбрасывает исключение, если доступ запрещен. Это может быть полезно, например, в веб-приложениях, где безопасность имеет первостепенное значение.

Подпишись 👉🏻 @KodduuPython 🤖
⚡️ Реактивное программирование с RxPy


import rx
from rx import operators as ops
from rx.scheduler import ThreadPoolScheduler
import requests

def fetch_url(url: str) -> str:
try:
response = requests.get(url)
response.raise_for_status()
return f"Fetched {url} with status {response.status_code}"
except requests.RequestException as e:
return f"Error fetching {url}: {str(e)}"

urls = [
"http://example.com",
"http://nonexistent.url",
"http://example.org"
]

# Настройка многопоточного планировщика
scheduler = ThreadPoolScheduler(max_workers=2)

# Создание реактивного потока
rx.from_(urls).pipe(
ops.map(lambda url: fetch_url(url)),
ops.do_action(lambda result: print(result))
).subscribe(
on_error=lambda e: print(f"Error occurred: {e}"),
on_completed=lambda: print("Fetching completed!")
)


📌 Этот код демонстрирует использование RxPy для асинхронного веб-скрапинга. Он создает поток URL-адресов, которые обрабатываются параллельно с помощью многопоточного планировщика. Каждый URL запрашивается, и результат выводится в консоль. Такой подход полезен для обработки большого количества запросов или выполнения асинхронных задач.

Подпишись 👉🏻 @KodduuPython 🤖
Forwarded from AIGENTTO
Два бота OpenClaw

Самим OpenClaw я обычно делаю апдейт его конфигов, но иногда это изменение что-то ломает. Поэтому у меня крутится всегда два бота, один чинит/меняет конфиги другого. А другой первого. Система абсолютно устойчива.

Подпишись 👉🏻 @aigentto 🤖
🚀 Улучшение производительности веб-приложений с помощью Asyncio


import asyncio
import aiohttp

async def fetch_data(url: str) -> dict:
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
if response.status == 200:
try:
return await response.json()
except aiohttp.ContentTypeError:
print("Получен ответ, не являющийся JSON")
else:
response.raise_for_status()

async def main():
urls = [
'https://api.example.com/data1',
'https://api.example.com/data2',
'https://api.example.com/data3',
]
tasks = [fetch_data(url) for url in urls]
results = await asyncio.gather(*tasks)
for result in results:
print(result)

# Запуск асинхронной программы
asyncio.run(main())


📌 Этот код демонстрирует, как использовать асинхронные операции для выполнения нескольких HTTP-запросов параллельно. С помощью библиотек aiohttp и asyncio можно значительно сократить время отклика при работе с сетевыми запросами, так как они выполняются без блокировки основного потока. Такая техника особенно полезна для веб-приложений, которые часто обращаются к внешним API.

Подпишись 👉🏻 @KodduuPython 🤖
👍2
🗄️ Оптимизация запросов к базам данных с использованием SQLAlchemy


from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship, joinedload

Base = declarative_base()

class Author(Base):
__tablename__ = 'authors'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)

class Book(Base):
__tablename__ = 'books'
id = Column(Integer, primary_key=True)
title = Column(String, nullable=False)
author_id = Column(Integer, ForeignKey('authors.id'))
author = relationship('Author', back_populates='books')

Author.books = relationship('Book', order_by=Book.id, back_populates='author')

# Настройка базы данных
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)

# Пример использования: Избегание избыточных запросов с помощью joinedload
def get_books_with_authors():
with Session() as session:
try:
books = session.query(Book).options(joinedload(Book.author)).all()
return [(book.title, book.author.name) for book in books]
except Exception as e:
print(f"Ошибка: {e}")

# Создание примеров данных
with Session() as session:
author = Author(name='J.K. Rowling')
book = Book(title='Harry Potter', author=author)
session.add(author)
session.add(book)
session.commit()

# Вызов функции
print(get_books_with_authors())


📌 Этот код демонстрирует использование SQLAlchemy для оптимизации запросов к базе данных с помощью joinedload. Эта техника уменьшает количество SQL-запросов за счет загрузки связанных данных в одно обращение, что особенно полезно при работе с большими объемами данных.

Подпишись 👉🏻 @KodduuPython 🤖
👍2
🌱 Симуляция клеточного автомата "Жизнь"


import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

def update(frame_num, img, grid, N):
new_grid = grid.copy()
for i in range(N):
for j in range(N):
# Считаем количество живых соседей
total = (grid[i, (j-1)%N] + grid[i, (j+1)%N] +
grid[(i-1)%N, j] + grid[(i+1)%N, j] +
grid[(i-1)%N, (j-1)%N] + grid[(i-1)%N, (j+1)%N] +
grid[(i+1)%N, (j-1)%N] + grid[(i+1)%N, (j+1)%N])
# Правила игры
if grid[i, j] == 1:
if total < 2 or total > 3:
new_grid[i, j] = 0
elif total == 3:
new_grid[i, j] = 1
img.set_data(new_grid)
grid[:] = new_grid[:]
return img,

def main():
N = 100 # Размер сетки
# Создаем начальную конфигурацию клеток
grid = np.random.choice([0, 1], N*N, p=[0.8, 0.2]).reshape(N, N)
fig, ax = plt.subplots()
img = ax.imshow(grid, interpolation='nearest')
ani = animation.FuncAnimation(fig, update, fargs=(img, grid, N),
frames=200, interval=300, save_count=50)
plt.show()

if __name__ == '__main__':
main()


📌 Этот код демонстрирует реализацию клеточного автомата "Жизнь" Джона Конвея. Алгоритм моделирует эволюцию клеток на двумерной сетке с простыми правилами: клетка остаётся живой, если у неё 2 или 3 живых соседа; иначе умирает. Новый организм рождается, если у пустой клетки ровно 3 живых соседа. Попробуйте изменить размер сетки или вероятность начального состояния, чтобы увидеть разные паттерны.

Подпишись 👉🏻 @KodduuPython 🤖
🤖 Создание Telegram чат-бота для автоматизации задач


import logging
from telegram import Update
from telegram.ext import Updater, CommandHandler, CallbackContext

logging.basicConfig(format='%(asctime)s - %(message)s', level=logging.INFO)

def start(update: Update, context: CallbackContext) -> None:
update.message.reply_text('Привет! Я ваш помощник для автоматизации задач.')

def remind(context: CallbackContext) -> None:
chat_id = context.job.context
context.bot.send_message(chat_id=chat_id, text='Время для вашего напоминания!')

def schedule_reminder(update: Update, context: CallbackContext) -> None:
try:
delay = int(context.args[0])
chat_id = update.message.chat_id
update.message.reply_text(f'Напоминание установлено на {delay} секунд!')
context.job_queue.run_once(remind, delay, context=chat_id)
except (IndexError, ValueError):
update.message.reply_text('Используйте: /remind <время в секундах>')

def main():
updater = Updater("YOUR_TELEGRAM_BOT_TOKEN", use_context=True)
dispatcher = updater.dispatcher

dispatcher.add_handler(CommandHandler("start", start))
dispatcher.add_handler(CommandHandler("remind", schedule_reminder))

updater.start_polling()
updater.idle()

if __name__ == '__main__':
main()


📌 Этот код создает Telegram чат-бота, который может устанавливать напоминания. Бот принимает команды от пользователя и позволяет задавать время для напоминания в секундах. Подходит для автоматизации задач, таких как планирование встреч или отправка напоминаний.

🛠 pip install python-telegram-bot

Подпишись 👉🏻 @KodduuPython 🤖
🔍 Анализ тональности текстов с помощью NLTK и машинного обучения


import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

nltk.download('punkt')
nltk.download('stopwords')

# Пример данных
texts = ["I love this product!", "This is the worst experience ever.", "Amazing quality, will buy again!", "Not worth the price."]
labels = [1, 0, 1, 0] # 1 - положительный, 0 - отрицательный

# Предварительная обработка текстов
def preprocess_text(text):
tokens = word_tokenize(text.lower())
tokens = [word for word in tokens if word.isalpha()]
return [word for word in tokens if word not in stopwords.words('english')]

processed_texts = [" ".join(preprocess_text(text)) for text in texts]

# Векторизация текстов и обучение модели
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(processed_texts)
X_train, X_test, y_train, y_test = train_test_split(X, labels, test_size=0.25, random_state=42)

model = MultinomialNB()
model.fit(X_train, y_train)

# Тестирование модели
predictions = model.predict(X_test)
print(f"Точность: {accuracy_score(y_test, predictions)}")

# Применение к новому тексту
new_text = "I am very happy with this purchase!"
processed_new_text = vectorizer.transform([" ".join(preprocess_text(new_text))])
print(f"Тональность: {'Положительная' if model.predict(processed_new_text)[0] == 1 else 'Отрицательная'}")


📌 Этот код использует NLTK для обработки текстов и Naive Bayes для анализа настроения. Это полезно для автоматической оценки отзывов и комментариев, что может помочь в бизнесе для понимания клиентской лояльности.

Подпишись 👉🏻 @KodduuPython 🤖
📊 Анализ настроений с NLTK


import nltk
from nltk.sentiment import SentimentIntensityAnalyzer

# Убедитесь, что необходимые ресурсы загружены
nltk.download('vader_lexicon')

def analyze_sentiment(text: str) -> str:
sia = SentimentIntensityAnalyzer()
sentiment_scores = sia.polarity_scores(text)
compound_score = sentiment_scores['compound']

if compound_score >= 0.05:
return 'Positive'
elif compound_score <= -0.05:
return 'Negative'
else:
return 'Neutral'

# Пример использования
text = "I absolutely love this new phone!"
print(f"The sentiment of the text is: {analyze_sentiment(text)}")


📌 Этот код использует библиотеку Natural Language Toolkit (NLTK) для анализа настроений в текстах. С помощью SentimentIntensityAnalyzer и лексикона VADER, он оценивает полярность текста, классифицируя его как позитивный, негативный или нейтральный. Это полезно для анализа отзывов клиентов или комментариев в социальных сетях.

Перед использованием кода установите NLTK: pip install nltk и загрузите ресурсы: nltk.download('vader_lexicon').

Подпишись 👉🏻 @KodduuPython 🤖
Forwarded from AIGENTTO
Проблемы внедрения AI в компании

Уже давно есть все инструменты - ИИ, ИИ агенты, мультиагентные системы, и наконец паттерн ReAct (OpenClaw). Последний реально может довольно быстро автоматизировать почти любые процессы.

Но реальное внедрение буксует, вот некоторые причины:
👉 Саботаж и отказ сотрудников что-то менять (автокассы в магазинах тоже прошли этот путь), ведь могут по сути автоматизировать и уволить сотрудников.
👉 Нехватка IT-опыта (когда этим занимаются не IT-специалисты, а вайбкодеры)
👉 Отказ IT-специалистов что-то менять (удивительно, но я сталкиваюсь с жутким консерватизмом со стороны самих IT-специалистов)
👉 Боязнь потратить много токенов (жадность?)

Но что показал эксперимент с roll-up компаниями типа Dwelly, оказывается, даже в небольших бизнесах, которые купил новый собственник, можно все перевести на рельсы AI OS под конкретную вертикаль, и прибыльность бизнеса вырастает с 10% до 40% в год (это в  Великобритании).

Вывод прост - только владелец может ИИзировать свою компанию, только тот, кто имеет абсолютный интерес и абсолютную власть в своей компании. К сожалению, даже топ-менеджеры здесь не подходят, потому что им выгодно много людей и отсутствие изменений, так они ничем не рискуют. 

Либо собственник должен нанять ИТ-драйвера, которому будет выделена доля в компании в случае успеха, и дать ему абсолютную власть, иначе опять не работает. Все остальное - полумеры, пилоты, которые, конечно, дают иногда результат, но чаще нет, чем да. 

Еще раз - суть в том, чтобы был человек, владеющий долей в компании и имеющий абсолютную власть. Опыт roll-ups показывает, что иногда сопротивляются даже собственники, а вот когда у тебя купили компанию, то все сопротивление заканчивается.

Если вам интересна тема, поставьте 👂, я напишу про реальные кейсы провалов и успехов.

Подпишись 👉🏻 @aigentto 🤖
🔄 Асинхронная работа с API в функциональном стиле


import asyncio
import aiohttp
from typing import List

# Функция для выполнения запроса к API
async def fetch_data(session: aiohttp.ClientSession, url: str) -> dict:
async with session.get(url) as response:
response.raise_for_status() # Проверка на ошибки HTTP
return await response.json() # Возврат JSON-ответа

# Функция для выполнения запросов ко всем URL-адресам
async def fetch_all_data(urls: List[str]) -> List[dict]:
async with aiohttp.ClientSession() as session:
tasks = [fetch_data(session, url) for url in urls] # Создание задач для всех URL
return await asyncio.gather(*tasks, return_exceptions=True) # Сбор результатов

# Пример использования
async def main():
urls = [
"https://jsonplaceholder.typicode.com/posts/1",
"https://jsonplaceholder.typicode.com/posts/2",
"https://jsonplaceholder.typicode.com/posts/3",
]
results = await fetch_all_data(urls)
for result in results:
print(result)

# Запуск асинхронного кода
asyncio.run(main())


📌 Этот код демонстрирует, как использовать асинхронное программирование для выполнения параллельных запросов к API. Мы применяем asyncio и aiohttp для асинхронной загрузки данных с нескольких URL-адресов. Такой подход улучшает производительность при взаимодействии с внешними API, что актуально для веб-приложений и сервисов, работающих с большими объемами данных.

Подпишись 👉🏻 @KodduuPython 🤖
🚀 Лайфхак: @lru_cache ускоряет рекурсию в тысячи раз!


import time
from functools import lru_cache

@lru_cache(maxsize=None) # Неограниченный кэш — храним ВСЕ уникальные результаты
def fibonacci(n: int) -> int:
"""Рекурсивное вычисление n-го числа Фибоначчи."""
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)

if __name__ == "__main__":
try:
n = 40 # Без кэша это заняло бы минуты или часы!
start = time.time()
result = fibonacci(n)
duration = time.time() - start

print(f"Фибоначчи({n}) = {result}")
print(f"Время с мемоизацией: {duration:.6f} сек")
except Exception as e:
print(f"Ошибка: {e}")


📌 Декоратор @lru_cache автоматически кэширует результаты вызовов функции. Повторные вычисления с теми же аргументами мгновенно берутся из памяти — никаких лишних рекурсий. Идеально для динамического программирования, алгоритмов на графах, парсинга и любых функций с повторяющимися подзадачами.

Подпишись 👉🏻 @KodduuPython 🤖
singledispatch: Полиморфизм по типу без if и классов!


from functools import singledispatch
from typing import Any

@singledispatch
def format_value(value: Any) -> str:
"""Обработка по умолчанию для неизвестных типов."""
return f"Неизвестный тип ({type(value).__name__}): {value}"

@format_value.register
def _(value: int) -> str:
return f"Целое число: {value} (в hex: {value:x})"

@format_value.register
def _(value: float) -> str:
return f"Число с плавающей точкой: {value:.4f}"

@format_value.register
def _(value: str) -> str:
return f"Строка длиной {len(value)}: \"{value[:50]}{'...' if len(value) > 50 else ''}\""

@format_value.register(list)
@format_value.register(tuple)
def _(value) -> str:
formatted = [format_value(item) for item in value[:3]]
return f"Коллекция ({len(value)} элементов): [{', '.join(formatted)}]"

if __name__ == "__main__":
try:
tests = [42, 3.14159, "Привет из Python!", [1, 2, "текст", 4.5]]
for test in tests:
print(format_value(test))
except Exception as e:
print(f"Ошибка: {e}")


📌 Декоратор @singledispatch превращает одну функцию в набор реализаций по типу первого аргумента. Никаких if isinstance — просто регистрируй обработчики. Идеально для парсеров данных, сериализаторов, валидаторов и API, где нужно красиво обрабатывать int, str, list, dict и свои классы.

Подпишись 👉🏻 @KodduuPython 🤖
🔥 @cached_property: Дорогие свойства вычисляются только один раз!


from functools import cached_property
import time
from typing import List

class DataAnalyzer:
def __init__(self, dataset: List[float]):
self.dataset = dataset

@cached_property
def mean(self) -> float:
"""Вычисляет среднее значение (имитация тяжелой операции)."""
print("🔄 Вычисляем среднее...")
time.sleep(1.2) # Имитация долгого расчёта
return sum(self.dataset) / len(self.dataset)

@cached_property
def std_dev(self) -> float:
"""Вычисляет стандартное отклонение."""
print("🔄 Вычисляем стандартное отклонение...")
time.sleep(0.9)
mean_val = self.mean
variance = sum((x - mean_val) ** 2 for x in self.dataset) / len(self.dataset)
return variance ** 0.5

if __name__ == "__main__":
try:
data = [2.5, 3.7, 6.8, 9.1, 4.3, 7.6]
analyzer = DataAnalyzer(data)

start = time.time()

print(f"Среднее: {analyzer.mean:.2f}")
print(f"Стандартное отклонение: {analyzer.std_dev:.2f}")

# Повторные обращения — мгновенно
print(f"Среднее (повтор): {analyzer.mean:.2f}")

print(f"Общее время: {time.time() - start:.2f} секунд")
except Exception as e:
print(f"Ошибка: {e}")


📌 @cached_property превращает метод в свойство, которое вычисляется только при первом обращении и потом кэшируется в экземпляре класса. Идеально для аналитики, обработки данных, машинного обучения и любых классов, где есть «тяжелые» свойства.

Подпишись 👉🏻 @KodduuPython 🤖
🔥 Retry-декоратор: спасение от нестабильных API и сетевых сбоев


import time
import random
from functools import wraps
from typing import Callable, Any

def retry(max_attempts: int = 5,
initial_delay: float = 1.0,
backoff: float = 2.0,
exceptions: tuple = (Exception,)):
"""Декоратор для автоматического повторения функции при ошибках."""
def decorator(func: Callable) -> Callable:
@wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
delay = initial_delay
last_exception = None

for attempt in range(1, max_attempts + 1):
try:
return func(*args, **kwargs)
except exceptions as e:
last_exception = e
if attempt == max_attempts:
break
print(f"⚠️ Попытка {attempt}/{max_attempts} не удалась: {e}")
print(f" Ждём {delay:.1f} сек...")
time.sleep(delay)
delay *= backoff # экспоненциальный backoff
raise last_exception
return wrapper
return decorator

# Пример использования
@retry(max_attempts=4, initial_delay=0.5, exceptions=(ValueError, ConnectionError))
def unreliable_api_call():
"""Имитируем нестабильный внешний сервис."""
if random.random() < 0.7: # 70% шанс ошибки
raise ConnectionError("Сервер временно недоступен")
return "Успешный ответ от API!"

if __name__ == "__main__":
try:
result = unreliable_api_call()
print(f" Результат: {result}")
except Exception as e:
print(f" Все попытки исчерпаны. Последняя ошибка: {e}")


📌 Этот декоратор автоматически повторяет функцию при указанных ошибках с увеличивающейся задержкой. Exponential backoff + возможность указывать исключения — must-have для работы с API, базами данных, парсерами и любыми внешними сервисами.

Подпишись 👉🏻 @KodduuPython 🤖
🆒2
🔥 Pydantic v2: Самая мощная валидация данных в Python


from pydantic import BaseModel, Field, ValidationError, EmailStr
from typing import List
from datetime import datetime

class User(BaseModel):
"""Модель пользователя с автоматической валидацией полей."""
id: int
username: str = Field(min_length=4, max_length=20)
email: EmailStr
age: int = Field(ge=16, le=99)
is_active: bool = True
skills: List[str] = Field(default_factory=list)

if __name__ == "__main__":
try:
data = {
"id": 101,
"username": "devpython",
"email": "hello@kodduu.ru",
"age": 27,
"skills": ["Python", "FastAPI", "Pydantic"]
}

user = User.model_validate(data)
print(f" Пользователь валиден: {user.username}")
print(f"Email: {user.email}")
print(f"Навыки: {user.skills}")

# Пример ошибки валидации
bad_data = data.copy()
bad_data["email"] = "неправильный email"
bad_data["age"] = 10
User.model_validate(bad_data)

except ValidationError as e:
print(" Ошибка валидации:")
for error in e.errors(include_url=False):
print(f" • {' -> '.join(map(str, error['loc']))}: {error['msg']}")
except Exception as e:
print(f"Ошибка: {e}")


📌 Pydantic автоматически проверяет типы, формат email, диапазоны значений, конвертирует строки в datetime и выдаёт понятные ошибки. Must-have для REST API, конфигов, форм, парсинга JSON и любой работы с внешними данными.

🛠 pip install pydantic

Подпишись 👉🏻 @KodduuPython 🤖
🔥 Loguru: Логирование, которое реально удобно использовать


from loguru import logger
import sys

# Удаляем стандартные обработчики, чтобы не было дублей
logger.remove()

# Вывод в консоль с цветами и красивым форматом
logger.add(
sys.stdout,
format="<green>{time:HH:mm:ss}</green> | <level>{level:8}</level> | {message}",
level="DEBUG",
colorize=True
)

# Запись в файл с автоматической ротацией
logger.add(
"logs/app_{time:YYYY-MM-DD}.log",
rotation="10 MB",
retention="7 days",
level="INFO"
)

@logger.catch
def risky_operation(x: int) -> float:
"""Пример опасной операции с автоматическим логированием исключений."""
return 100 / x

if __name__ == "__main__":
try:
logger.info("🚀 Приложение успешно запущено")
logger.debug("Это отладочное сообщение для разработчика")

result = risky_operation(25)
logger.success(f" Результат расчёта: {result}")

# Вызовем ошибку специально
risky_operation(0)

except Exception as e:
logger.error(f" Неожиданная ошибка: {e}")


📌 Loguru заменяет стандартный logging одной строкой настройки. Красивые цвета, автоматическая запись в файл, ротация логов, перехват всех исключений через @logger.catch — и никаких boilerplate-кодов. Идеально для скриптов, CLI, веб-сервисов и продакшена.

🛠 pip install loguru

Подпишись 👉🏻 @KodduuPython 🤖
👏1
Forwarded from AIGENTTO
Проблема автоматизации чего-либо — это не технология

Начну издалека: автоматизация чего-либо — это не новшество: 100+ лет назад человек переходил от лошади к трактору, примерно в то же время — от ручного труда к ткацкому станку и т. д.

Каждый раз производительность росла, и ВВП на душу населения рос, что приводило к увеличению благ на каждого человека. Поэтому страх, что всех уволят, сильно преувеличен, а точнее, это может стать правдой в моменте, но в долгую AI увеличит блага на душу населения, и так или иначе они все равно будут распределены между людьми.

Теперь переместимся чуть ближе в 2014 год: я нанят в "Большой зеленый банк" как Head автоматизации всего QA, то есть автоматизировать все тестирования всех систем компании (200+ очень разношерстных приложений). Тогда еще не было современного AI, хотя эксперимент мы проводили по автоматизации через распознавание экрана с помощью библиотек Google, все на самом деле даже работало, но со скроллами и legacy интерфейсами были проблемы 🤷‍♂️

Короче, взяли на тот момент уже стандарт BDD+Java+Selenium driver для всего зоопарка Web+Rest API и уникальные всякие штуки для других областей — была там и знаменитая роборука, которая физически тестировала карточки, вставляя их в терминал 🙂

В тот момент проблемой была не технология, хотя с ней были отдельные решаемые сложности. То же самое сейчас - AI не является блокером, в том же паттерне ReAct (читай OpenClaw) может делать 95% дел которые люди делают за компом.

Тогда главной проблемой были люди, а точнее, их интересы: 5 топ-менеджеров компании управляли большими секторами тестирования и с удовольствием выбивали себе огромные бюджеты на найм ручных тестировщиков. Я, по сути, был призван остановить этот бесконечный найм в ручное тестирование и, по факту, стал главным врагом как минимум этих топ-5 менеджеров, а как максимум — всех 2000+ ручных тестировщиков, которые хотели получать зарплату, тыкать кнопки и не очень хотели учить Java и переходить на автоматизацию.

Для понимания накала страстей, в один момент один из этих топ-5 менеджеров то ли в шутку, то ли всерьёз начал на меня кидаться, так сильно я ему мешал 🙂

Я бы хотел сказать, что победили автоматизация и здравый смысл, но так не работает на больших системах, тем не менее через 4 года уровень автоматизации QA вырос с 0 до 30+%, с учетом того, что за это время внедрили еще 10-ки новых приложений и сам объем тестирования вырос в разы. То есть цель автоматизации в 100% была убегающей: вчера 100% — это было 100 000 тестов, завтра — это 200 000 тестов и так далее.

Почему всё-таки получилось сдержать рост ручного труда и, по факту, сэкономить до 300 млн руб. в год на ручном тестировании? Причина была не в том, что я гений автоматизации, причина была не в технологии. Причина была только в том, что мне дали полный ownership, и директор департамента требовал с меня результат, но не лез в то, как и с помощью чего я буду это делать: вначале там была технология и команда из XIX века, я её убрал за один день, несмотря на большую любовь многих к этой технологии и команде, а любовь была потому, что ребята были приятные и никого сильно не напрягали.

И главное — директор департамента дал мне право на внедрение изменений и всегда его поддерживал. Да, конечно, я не шел напролом, я договаривался с теми 5 топ-менеджерами, но у меня было абсолютное право финального решения по всем вопросам автоматизации.

Только полный ownership и право принимать финальные решения по вопросам в моей зоне ответственности позволили добиться большого результата.

Следующими постами я расскажу, как проваливают текущую AI-автоматизацию большие и малые компании. Нельзя просто дать денег и не дать ownership. Нельзя просто нанять ИТшников, чтобы они сделали инструмент, и все: они его сделают, но он будет лежать в сторонке, и деньги будут потрачены зря.

Подпишись 👉🏻 @aigentto 🤖
🔥 Typer: CLI-приложения уровня профи за 5 минут


import typer
from typing import Optional

app = typer.Typer()

@app.command()
def greet(
name: str = typer.Argument("Мир", help="Кого приветствуем"),
times: int = typer.Option(1, "--times", "-t", help="Сколько раз повторить"),
uppercase: bool = typer.Option(False, "--upper", help="В верхнем регистре")
):
"""Приветствует пользователя с кастомными опциями."""
message = f"Привет, {name}!"
if uppercase:
message = message.upper()

for _ in range(times):
typer.echo(message)

@app.command()
def calculate(
a: int,
b: int,
operation: str = typer.Option("add", help="Операция: add, sub, mul")
):
"""Простой калькулятор через CLI."""
if operation == "add":
result = a + b
elif operation == "sub":
result = a - b
elif operation == "mul":
result = a * b
else:
typer.echo(" Неизвестная операция")
raise typer.Exit(code=1)
typer.echo(f" Результат: {result}")

if __name__ == "__main__":
try:
app()
except Exception as e:
typer.echo(f" Ошибка: {e}")


📌 Typer автоматически создаёт красивый CLI с подсказками, валидацией типов, автодополнением и помощью — всё из type hints. Никаких argparse и click вручную. Идеально для утилит, dev-tools, скриптов автоматизации и внутренних команд.

🛠 pip install typer

Подпишись 👉🏻 @KodduuPython 🤖
🔥 Rich: Красивый терминал за 10 секунд


from rich.console import Console
from rich.table import Table
from rich.progress import track
import time
from typing import List

console = Console()

def process_items(items: List[str]):
"""Пример обработки списка с красивым прогресс-баром и таблицей."""
table = Table(title="Результат обработки")
table.add_column("Элемент", style="cyan")
table.add_column("Статус", style="green")

for item in track(items, description="Обрабатываем элементы..."):
time.sleep(0.3) # имитация работы
status = " Успешно"
table.add_row(item, status)

console.print(table)

if __name__ == "__main__":
try:
data = ["user1.json", "config.yaml", "data.csv", "report.pdf", "logs.txt"]
console.print("[bold magenta]🚀 Запуск обработки файлов[/bold magenta]")
process_items(data)
console.print("[bold green] Все задачи выполнены![/bold green]")
except Exception as e:
console.print(f"[bold red]Ошибка: {e}[/bold red]")


📌 Rich позволяет легко создавать цветной вывод, таблицы, прогресс-бары, статусы и даже markdown в терминале. Один из самых любимых инструментов Python-разработчиков для CLI-приложений, скриптов и дашбордов.

🛠 pip install rich

Подпишись 👉🏻 @KodduuPython 🤖
🔥 httpx: Современная замена requests — быстрее, удобнее и с лучшей обработкой ошибок


import httpx
from typing import Dict, Any

def get_user_info(user_id: int) -> Dict[str, Any]:
"""Пример безопасного запроса к публичному API с httpx."""
url = f"https://jsonplaceholder.typicode.com/users/{user_id}"

# httpx.Client автоматически управляет соединениями и таймаутами
with httpx.Client(timeout=10.0) as client:
try:
response = client.get(url)
response.raise_for_status() # автоматически бросает исключение при 4xx/5xx
return response.json()
except httpx.HTTPStatusError as e:
print(f"HTTP ошибка {e.response.status_code}: {e.response.text}")
raise
except httpx.RequestError as e:
print(f"Ошибка запроса (таймаут/соединение): {e}")
raise

if __name__ == "__main__":
try:
user = get_user_info(1)
print(f" Пользователь: {user['name']}")
print(f"Email: {user['email']}")
print(f"Город: {user['address']['city']}")
except Exception as e:
print(f" Не удалось получить данные: {e}")


📌 httpx — это современный HTTP-клиент с автоматической поддержкой JSON, context manager, таймаутами и отличной обработкой ошибок. Работает и в синхронном, и в асинхронном режиме. Идеально для API, парсинга, микросервисов.

Подпишись 👉🏻 @KodduuPython 🤖