Задать вопрос
  • Как сделать SSH проброс портов как сервис или задание в windows 10?

    Vindicar
    @Vindicar
    RTFM!
    В винде служба должна дёргать специальное API при запуске и в ходе работы, поэтому нельзя просто запустить произвольный процесс как службу.

    Как правильно указали выше, нужно обернуть процесс в NSSM - он возьмёт на себя взаимодействие с API, а также запуск/мониторинг/перезапуск вашего процесса.

    Запуск через планировщик проще, но у запуска через службу есть то преимущество, что можно настроить другие службы как зависимые от туннеля.
    Ответ написан
    Комментировать
  • Дублирование логов в python logging?

    Vindicar
    @Vindicar
    RTFM!
    Читаем доки на logging, там уже всё есть, что нужно.
    Создаёшь класс-потомок logging.Handler, в котором реализуешь запись сообщений журнала в базу. Но стоит поискать, может, уже есть сторонняя либа которая как раз такой класс даёт.
    Дальше делаешь кастомный класс-фильтр, в котором отличаешь нужную инфу от ненужной.
    При настройке логгинга добавляешь к нужному логу свой хэндлер, к хэндлеру уже прикручиваешь свой фильтр. Если надо, по аналогии можно к хэндлеру прикрутить ещё и свой Formatter для нестандартного оформления.
    Ответ написан
    4 комментария
  • Как технически работает проксирование через ssh?

    Vindicar
    @Vindicar
    RTFM!
    по аналогии с curl, или каким-нибудь wget,

    Неправильно. SSH-сервер не имеет понятия, на каком птичьем языке говорит тот, кто использует порт. Он тупо туннелирует пакеты как массив байт. Единственная "бонусная" функциональность - это SOCKS5 прокси в SSH-клиенте для динамического проброса портов, а для более статического проброса (в любую сторону) и это не используется.
    Ответ написан
    Комментировать
  • В каком слое DTO объект преобразовывать в словарь перед сохранением в БД?

    Vindicar
    @Vindicar
    RTFM!
    Я бы отдавал в репозиторий DTOшки. Что там в словаре должно быть и как преобразовывать - это имхо как раз его вопрос, вышележащему коду это знать незачем.
    Ответ написан
    Комментировать
  • Почему модель обнаружения объектов YOLO работает медленно?

    Vindicar
    @Vindicar
    RTFM!
    Первый момент тебе уже подсказали - потоки в питоне не помогут.
    Второй момент - я фз зачем тебе supervision, попробуй сначала голый yolo, и в частности, попробуй запустить его на видяхе.
    Третий момент - yolo бывают разного размера. Чем больше модель, тем точнее, но и тем медленнее.

    Вот пример, с которым я работал

    """
    Принимаем видео с вебкамеры, и пытаемся сегментировать его с помощью YOLOv8.
    Выводим в окне маски объектов.
    Потребуется установить пакеты следующей командой:
    pip install ultralytics
    """
    from sys import argv
    from pathlib import Path
    
    import numpy
    import cv2
    import torch
    
    COLORS = [
        (64, 128, 128),
        (128, 64, 64),
        (64, 128, 64),
        (64, 64, 128),
        (128, 64, 128),
    ]
    
    VIDEO_SOURCE = 0  # 0 - вебкамера. Строка - имя файла или URL видео потока.
    script_dir = Path(argv[0]).parent.resolve()
    # определяем, на каком устройстве будет выполняться модель
    device = (
        'cuda' if torch.cuda.is_available() else
        'mps' if torch.backends.mps.is_available() else
        'cpu'
    )
    
    import ultralytics
    
    
    def text_at(  # рисует текст по центру
            img: numpy.ndarray,  # на чём
            text: str,  # что
            pos: tuple[int, int],  # где центр
            font=cv2.FONT_HERSHEY_COMPLEX,  # каким шрифтом
            font_scale: float = 1.0,  # в каком масштабе (не кегль!)
            font_thickness: int = 2,  # насколько толстые линии
            text_color=(255, 0, 0),  # каким цветом
            bg_color=None  # на каком фоне
            ):
        x, y = pos
        (text_w, text_h), _ = cv2.getTextSize(text, font, font_scale, font_thickness)
        if bg_color is not None:
            cv2.rectangle(img,
                          (x - text_w // 2, y - text_h // 2),
                          (x + text_w // 2, y + text_h // 2),
                          bg_color, -1)
        cv2.putText(img,
                    text,
                    (x - text_w // 2, y + text_h // 2 - 1),
                    font, font_scale, text_color, font_thickness)
    
    
    model = ultralytics.YOLO(
        script_dir / 'yolov8m-seg.pt'  # имя файла модели указывает на её тип
    )
    
    # цикл работы с видео
    video = cv2.VideoCapture(VIDEO_SOURCE)
    if not video.isOpened():
        raise SystemExit('Не удалось открыть источник видео')
    overlay_image: numpy.ndarray = numpy.zeros(  # изображение-оверлей для отображения разметки кадра
        (
            int(video.get(cv2.CAP_PROP_FRAME_HEIGHT)),
            int(video.get(cv2.CAP_PROP_FRAME_WIDTH)),
            3
         ),
        numpy.uint8
    )
    
    try:
        while True:
            success, frame = video.read()  # читаем кадр
            if not success:
                raise SystemExit('Видео закончилось')
            # для YOLO предобработка не требуется
            outputs = model.predict(  # получаем отклик сети
                source=frame,
                conf=0.15,  # минимальная степень уверенности в объекте
                device=device,  # устройство
                verbose=False  # чтобы не спамило логом в консоль
            )
            output = outputs[0]  # модель всегда возвращает список результатов, даже для одного изображения
            if output.masks is not None:  # если были получены маски
                masks = output.masks.cpu().xy  # список масок как координат вершин многоугольников (контуров)
                boxes = output.boxes.cpu()  # ограничивающие прямоугольники
                image_output = []  # список аннотаций, которые нужно будет вывести
                for box, mask in zip(boxes, masks):  # формируем список аннотаций
                    class_id = int(box.cls.item())  # номер класса, соответствующего очередной области
                    class_name = model.names[class_id]  # имя класса, соответствующее области
                    pts = numpy.array(mask).astype(numpy.int32)  # массив координат точек контура Nx2
                    image_output.append((class_id, class_name, pts))
                overlay_image.fill(0)  # очищаем изображение-оверлей
                for (class_id, class_name, pts) in image_output:  # закрашиваем каждую маску цветом
                    cv2.fillPoly(
                        img=overlay_image,
                        pts=pts[numpy.newaxis, ...],  # для работы fillPoly() массив должен быть вида 1xNx2
                        color=COLORS[class_id % len(COLORS)],  # цвет массива определяем по номеру класса для стабильности
                    )
                for (class_id, class_name, pts) in image_output:  # выводим надписи отдельным циклом, чтобы их не закрасило
                    M = cv2.moments(pts)  # вычисляем моменты контура
                    # используем их для расчёта координат центроида контура
                    cX = int(M["m10"] / M["m00"])
                    cY = int(M["m01"] / M["m00"])
                    # выводим имя класса
                    text_at(overlay_image, class_name,
                            (cX, cY),
                            text_color=(255, 255, 255),
                            bg_color=(1, 1, 1))
                # накладываем оверлей с разметкой на кадр
                alpha = 0.8  # вес оверлея, чем меньше - тем он прозрачнее.
                apply = overlay_image > 0  # изменяем только те части кадра, где есть оверлей
                frame[apply] = alpha * overlay_image[apply] + (1 - alpha) * frame[apply]  # обожаю numpy
            cv2.imshow('Press Esc to exit', frame)  # показываем результат
            if cv2.waitKey(10) == 27:  # если пользователь нажал Esc, выходим
                break
    finally:
        video.release()  # не забываем отключиться от источника видео в итоге

    Ответ написан
    Комментировать
  • Никакой vpn не работает ни amnezia vpn wg, tor, vless + reality?

    Vindicar
    @Vindicar
    RTFM!
    Имей ввиду пару вещей.
    1. Пинг -1 в клиенте-обёртке типа V2RAY - ни о чём. У меня такой пинг уже года полтора, а туннель работает.
    2. Мог засветить IP адрес если ходил через туннель на российские/беларусские/иже с ними ресурсы. Моему серверу некоторое время назад хорошо так резали скорость из-за неосторожности. Сменил IP на VPS, где туннель поднят, теперь осторожничаю. Настроил роутинг в клиенте.
    3. Есть разница, как настроен REALITY. Насколько я понимаю, self-steal (когда маскируешься под собственный сайт, развёрнутый на том же домене) работает более надёжно.
    Ответ написан
  • Как сделать нажатие кнопки при парсинге в python?

    Vindicar
    @Vindicar
    RTFM!
    Да, можно.
    Используй консоль разработчика в браузере, чтобы понять, какой именно запрос отправляется на сервер. Обычно нужно выяснить метод (GET/POST), URL, параметры, тело. Либа requests и аналоги позволяют всё это задать, просто почитай доки по конкретной теме.
    Ответ написан
    Комментировать
  • Как запустить отдельный скрипт в субпроцессах, получив интерфейс типа concurrent.futures.Executor?

    Vindicar
    @Vindicar Автор вопроса
    RTFM!
    Плохая новость - готового решения сформулированной в вопросе задачи, похоже, нет. Нужно использовать брокеры или что-то подобное.

    Хорошая новость - я снова попался на проблему XY. Всё нормально, если спрятать тяжёлые импорты в теле основной программы вот так:
    # ------------------- main.py -------------------
    async def main():
        import urllib3  # как бы тяжелый модуль, нужный только главному скрипту
        import importlib
        ...  # получаем исходные данные
        # динамически подтаскиваем нужный модуль
        mod = importlib.import_module('modules.submodule')  
        m = mod.Module(x=-1)  # используем класс из него
        # этот метод под капотом использует мультипроцессинг
        results = await m.run_tasks(some_data)
        ...  # что-то делаем с результатами
    
    if __name__ == '__main__':
        import asyncio
        asyncio.run(main())
    
    # ------------------- modules/submodule.py -------------------
    import asyncio
    import concurrent.futures
    import time
    import sys
    import os
    
    class Module:
            def __init__(self, x):
            self.x = x
    
        def __repr__(self) -> str:
            return f'{self.__class__.__name__}(x={self.x!r}) {hex(id(self)).upper()} @ {os.getpid()}'
    
        async def run_tasks(self, data):
            self.x = len(items)
            print(f'Using instance {self!r}')
            loop = asyncio.get_running_loop()
            pool = concurrent.futures.ProcessPoolExecutor(4)  # где будем исполнять таск
            # готовим таски к исполнению
            futures = [loop.run_in_executor(pool, self.worker_func, item) for item in data]
            # ждём их завершения
            return await asyncio.gather(*futures, return_exceptions=True)
    
        def worker_func(self, item):
            # инстансы разные, разумеется, но их состояние, похоже, клонируется в новые процессы...
            print(f'Using instance {self!r}. urllib3', 'is' if 'urllib3' in sys.modules else 'is not', 'present', flush=True)
            time.sleep(0.1)  # имитируем напряжённую работу
            return repr(item)
    Ответ написан
    Комментировать
  • Как правильно спроектировать эту функцию?

    Vindicar
    @Vindicar
    RTFM!
    Ты неверно понимаешь, как работает overload. Это тупо подсказка для IDE - "эту функцию можно вызывать вот так, и она вернёт вот это, или же вот этак, и она вернёт вон то". Тело функции, завёрнутой в overload, игнорируется. А последнее объявление функции не должно иметь overload, и оно как раз и будет реализовывать все варианты. Так что итоговая реализация всё равно будет иметь if внутри. Да и в твоём случае overload никчему, так как другие-то параметры не отличаются.

    Ты мог бы, конечно, упороться, и сделать functools.singledispatch - при условии, что ты message переставишь первым параметром. Но, имхо, в этом нет практической необходимости, всё это делается в разы проще.

    async def show_cpanel(
        state: FSMContext,
        message_source: typing.Union[Message, CallbackQuery]  # значение одного из двух типов, но обязательное!
    ) -> None:
        '''
        Send control panel to current user
        '''
        this_user = message_source.from_user.id  # и Message, и CallbackQuery имеют from_user
        message = message if isinstance(message_source, Message) else message_source.message
    
        profile_is_visible = await req.check_profile_visible(this_user)
        message_id = (await message.answer(
            '<b>' + _('Панель управления') + '</b>',
            parse_mode='HTML',
            reply_markup=kb.control_panel(profile_visible=profile_is_visible)
        )).message_id
        await state.update_data(cpanel_message_id=message_id)
        logger.info(f'Admin #{this_user} opened control panel')

    Вот и всё. Твой вариант
    callback_query: Optional[CallbackQuery] = None,
        message: Optional[Message] = None,

    плох тем, что из него совершенно не очевидно, что хотя бы один из параметров не должен быть None.

    EDIT: Хотя я не вполне понял идею насчёт callback.message. Если я верно помню, этот атрибут хранит ссылку на сообщение от бота, содержащее кнопку, для которой был вызван callback. Поэтому его from_user по идее будет заведомо указывать на бота. Так что да, лучше последуй совету Everything_is_bad и переделай функцию так, чтобы она принимала пользователя и сообщение отдельными параметрами. А их значения определяй там, где ты функцию вызываешь.
    Ответ написан
    5 комментариев
  • SSH +reverse https proxy, при чём тут openSSL(VPN)?

    Vindicar
    @Vindicar
    RTFM!
    Ну для начала, OpenSSL - это (если на пальцах) библиотека+утилита для шифрования трафика. Ею или её аналогами пользуется примерно всё, что хочет установить шифрованный канал. Помимо использования её как библиотеки, ты можешь использовать её как утилиту, в духе "слушай адрес+порт A.B.C.D:X, все входящие подключения расшифровывай(для сервера)/шифруй(для клиента), после чего перенаправляй на адрес+порт E.F.G.H:Y". Тогда таким образом можно создать шифрованный туннель, через который пускать трафик какого-то другого приложения.

    Но постановка задачи вызывает вопросы. Что значит "только по HTTPS"? Каким именно образом это правило контролируется? Не зная ответа на этот вопрос, решения не найдёшь.

    Если "нужно увидеть TLS-хэндшейк", то да, подход на базе OpenSSL может сработать, но его нужно будет настроить и на сервере, и на клиенте. На сервере - OpenSSL должна слушать порт и перенаправлять расшифрованные соединения на порт SSH сервера. На клиенте - OpenSSL должна слушать порт, и пробрасывать зашифрованные соединения на адрес+порт, который случает OpenSSL на сервере.

    Но имей ввиду, что SSH сервера вроде OpenSSH (но вроде не Dropbear) и так используют OpenSSL для защиты своего трафика. Хотя при этом всё равно есть различия между тем, как устанавливается соединение при HTTP+TLS и при SSH, так что может в таком туннеле и будет смысл.

    Но соответствие HTTPS может контролироваться более жёстко. Нужно использовать заданный хостером HTTP-прокси/реверс-прокси для работы с сетью? Или, скажем, слишком долго висящие соединения принудительно рвутся? Или TLS-хэндшейк проверяется на соответствие популярным браузерам? Во всех этих случаях простым туннелем ты не обойдёшься.
    Ответ написан
    6 комментариев
  • Как в командной строке сохранить полный путь к файлу для вставки его в другом месте при смене директории?

    Vindicar
    @Vindicar
    RTFM!
    Если задача всплывает часто, освой Midnight Commander или иной двухпанельный файловый менеджер. Он запускается в терминальном режиме и прекрасно подходит для таких ситуаций.
    Ответ написан
    Комментировать
  • Можно ли применить условное форматирование к графику/диаграмме?

    Vindicar
    @Vindicar
    RTFM!
    Нет, нельзя. Построить два графика (один только с растущими интервалами, один только с убывающими) тоже тяжело, так как число точек в них не будет совпадать с числом точек в исходных данных.
    Если ты переделаешь исходные данные в такой вид:
    X Y
    1 10
    1 10
    2 15
    2 15
    3 12
    3 12
    ...
    То тогда можно будет провернуть трюк с двумя сериями в одних осях. Но это тоже тот ещё гемморой.
    Ответ написан
  • Не копятся ли строки в памяти при работе с python?

    Vindicar
    @Vindicar
    RTFM!
    Попробовал накидать скрипт для проверки:
    import tracemalloc
    
    tracemalloc.start()
    
    for i in range(4):
        snapshotA = tracemalloc.take_snapshot()
        s = f'Строка №{i+1}'
        snapshotB = tracemalloc.take_snapshot()
        s = s.replace('Строка', 'Другая строка')
        snapshotC = tracemalloc.take_snapshot()
        print('>', s)
        statab = snapshotB.compare_to(snapshotA, 'lineno')
        print("[A->B]")
        for stat in statab[:10]:
            if stat.traceback[0].filename == __file__:
                print(stat)
        statbc = snapshotC.compare_to(snapshotB, 'lineno')
        print("[B->C]")
        for stat in statbc[:10]:
            if stat.traceback[0].filename == __file__:
                print(stat)

    Если я верно интерпретирую вывод, неиспользуемые строки "умирают" на каждой итерации.
    Но ситуация усложняется, если на них есть ссылки в других местах, особенно если имеют место циклические ссылки.
    Ответ написан
  • Как вывести время в шаблоне Django в соответствии с часовым поясом?

    Vindicar
    @Vindicar
    RTFM!
    Не работал с джангой, но...
    time = datetime.time(11, 00, 0, tzinfo=timezone(timedelta(hours=0)))

    Это время в UTC.
    А вот что используется при выводе... может, как раз твоё TIME_ZONE = 'Europe/Moscow'?
    Ответ написан
  • Извлечение таблиц со спецификациями из PDF чертежей металлоконструкций - решаемо? Или я встрял?

    Vindicar
    @Vindicar
    RTFM!
    Имхо без комбинации подходов ловить вообще нечего. Найди инструмент (и набор предобработок), который найдёт тебе разметку таблиц, выдерни содержимое отдельных ячеек, и подбирай предобработки+инструмент, который будет распознавать ячейки.
    Ответ написан
    Комментировать
  • Как ловить exceptions в библиотеке которую я не использую напрямую?

    Vindicar
    @Vindicar
    RTFM!
    где я его должен ловить учитывая что саму urllib3 я через import не подключаю

    Ну так подключай её в свой код и лови в своём коде. from urllib3.exceptions import LocationParseError
    Раз requests её уже использует, затраты на import в твоём скрипте будут околонулевые.
    requests поступает вполне логично, не изобретает велосипед (т.е. своё исключение с тем же смыслом), а задействует тип исключения из используемой стандартной библиотеки языка.
    Ответ написан
    Комментировать
  • Какую библиотеку лучше использовать для discord бота на python?

    Vindicar
    @Vindicar
    RTFM!
    discord.py уже давно начал деятельность обратно. На момент написания этого коммента на гитхабе последний коммит 3 месяца назад, issue закрыт считанные дни назад.
    Ответ написан
    Комментировать
  • Почему возникают ошибки несовместимости версий Python и как решить проблему с установкой PySimpleGUI?

    Vindicar
    @Vindicar
    RTFM!
    Разобраться, какие версии библиотек всё-таки нужны. Ты не написал, для каких пакетов возникает ошибка.
    Возможно, стоит смягчить требования - например, не требовать определённую версию пакета, а любую, или ограничится только первой цифрой. Скажем, вместо somepackage==1.2.3 указать somepackage==1.* или вообще somepackage без версии.
    Ответ написан
    Комментировать
  • Можно ли улучшить приложенный AutoEnum (см. код ниже) в python?

    Vindicar
    @Vindicar
    RTFM!
    Очевидно, что декоратор должен находить и заменять объявленные константы сам, так как всё остальное уже отработало.
    Если хочешь полагаться на метакласс, ему можно передавать аргументы при наследовании. ЕМНИП:
    class MyMeta(type):
        def __new__(metacls, name, bases, namespace, **kwargs):
            print(f"__new__(): {name}({kwargs})")
            return super().__new__(metacls, name, bases, namespace)
    
    class MyBase(metaclass=MyMeta):  # __new__(): MyBase({})
        pass
    
    class MyClass(MyBase, foo='bar'):  # __new__(): MyClass({'foo': 'bar'})
        pass

    А вообще я не вижу выигрыша в твоём классе. Статическая типизация, к слову, барахлить не начнёт? Среда разработки будет правильно опознавать TestAutoEnum.FIRST как экземпляр TestAutoEnum?
    Ответ написан
    Комментировать