• Почему при запуске Django тестов через ./manage.py не работают патчи?

    Assargin
    @Assargin
    Перед ответом смотрю наличие ✔ в ваших вопросах
    Скорее всего, вы патчите что-то, что в случае вызова через manage.py импортится до того, как запатчится.
    Скорее всего, вы пытаетесь пропатчить исходную сущность в сторонней либе (или даже стандартной), а надо патчить то, что импортируете к себе в модуль, сущность из которого тестируете.

    Например, у вас есть ф-я mypackage.mymodule.foo, где используется urlopen из urllib:
    from urllib.request import urlopen
    
    def foo():
      ...

    И для этой ф-и вы хотите написать тесты, запатчив вызов urlopen.
    Потенциально нерабочий вариант:
    @patch("urllib.request.urlopen")
    def test(urlopen):
      ...

    Точно рабочий вариант:
    @patch("mypackage.mymodule.urlopen")
    def test(urlopen):
      ...


    UPD: а вообще, видеть бы код теста и самой тестируемой ф-и
    Ответ написан
  • Где взять VPS для VPN, чтобы работал ChatGPT?

    Assargin
    @Assargin Автор вопроса
    Перед ответом смотрю наличие ✔ в ваших вопросах
    Через день после того, как я задал этот вопрос, на хабре была опубликована очень релевантная статья: автор обозревает недорогие тарифы для VPN у различных хостинг-провайдеров с возможностью оплаты банковской картой РФ.

    В конце статьи автор дополнил, что chat.openai.com со всех инстансов у него открывается без 403-х.
    Ответ написан
  • Резонно ли использовать один Celery для множества приложений?

    Assargin
    @Assargin
    Перед ответом смотрю наличие ✔ в ваших вопросах
    Celery - это штука, которая умеет выполнять некоторые, помеченные вами специальным декоратором функции, "где-то там", асинхронно, в N единиц параллельности, и т.д. Но ключевой момент в кодовой базе. Если вы можете выделить код заданий в отдельный проект, так, что эти функции-задания будут работать - смело прикручивайте к нему Celery, оборачивайте в контейнер - и вот у вас микросервис заданий.

    Однако лично я с таким не сталкивался, обычно кодовая база одна. В монолите на джанге ли, или в сервисе на фласке - задания писались мной так или иначе как часть одного или нескольких сервисов, и использовался код проекта: модели БД, либы.

    Вполне реален и нормален сценарий, когда вы монолит подробили, к примеру, на 5 микросервисов. Задания также растеклись по ним, оказавшись в 3 из 5 сервисов. Значит, к тем сервисам, в который есть задания, нужно рядом поднимать контейнера на тех же образах, уже в качестве воркеров заданий.
    Ответ написан
    Комментировать
  • Как организовать микро слипы в задаче celery?

    Assargin
    @Assargin
    Перед ответом смотрю наличие ✔ в ваших вопросах
    Явное указание ETA ради лимитирования - так себе механизм. Если воркер вдруг умрёт, то после перезапуска новому воркеру придут на выполнение сразу все "опоздавшие" задания, и чем дольше воркер лежал, тем больше будет пачка. Также, сложно регулировать проставление абсолютного значения ETA, если вдруг придётся делать это одновременно для нескольких потоков рассылок, чтобы они, выполняясь параллельно, не выбирали весь лимит.

    Ещё в Celery есть механизм rate limit на тип таска, правда, он работает только в пределах 1 воркера.

    Т.е. в тех нечастых случаях, когда на 1 очередь запускается более 1 воркера + для случаев, когда лимит с типом таска матчится не 1:1 (например, лимит на API общий, а методов в этом API может быть 100500, каждый дёргается с помощью отдельного типа таска) - есть реализация на основе token bucket.
    Ответ написан
    Комментировать
  • Как общаться двум компам которые находятся за своими NAT?

    Assargin
    @Assargin
    Перед ответом смотрю наличие ✔ в ваших вопросах
    NAT Traversal, STUN, TURN.

    Совсем без чего-нибудь промежуточного с белым адресом, хотя бы чисто для первоначальной инициализации соединения и поиска "компов" друг другом - никак.
    Даже в WebRTC в самом простом случае есть сигнальный сервер, через который пиры обмениваются кандидатами, и иногда получается без излишеств типа STUN/TURN, но зачастую только в идеальном мире.
    Ответ написан
    1 комментарий
  • Как отсортировать числовые строки вида "Х.Х.Х"?

    Assargin
    @Assargin
    Перед ответом смотрю наличие ✔ в ваших вопросах
    Чтобы не заниматься программированием в PostgreSQL, в котором натуральной сортировки для такого случая я не нашёл, решением может быть хранить ваши строки не as is, а отдельно буквы и массив цифр, т.е. вместо одного поля VARCHAR для всей строки использовать 2 поля: строка VARCHAR для букв и массив чисел INTEGER[]:

    CREATE TABLE test (
      "letters" VARCHAR(20),
      "digits" INTEGER[]
    );
    
    INSERT INTO test
      ("letters", "digits")
    VALUES
      ('ЧС', '{1, 1}'),
      ('ЧС', '{1, 2}'),
      ('ЧС', '{1, 3, 1}'),
      ('ЧС', '{1, 10}'),
      ('ЧС', '{1, 11, 1}'),
      ('П', '{1, 1}'),
      ('П', '{1, 2}'),
      ('П', '{2, 10}');


    SQL тогда получается банальным - сортировка просто по этим двум полям, а собрать нужную строку можно прямо в запросе:
    SELECT letters || '.' || array_to_string(digits, '.') from test order by letters, digits;


    Поиграться: https://www.db-fiddle.com/f/kGBapu86EXBuMZ6Bh6mGfe/1
    Ответ написан
    Комментировать
  • Почему не сохраняется m2m?

    Assargin
    @Assargin
    Перед ответом смотрю наличие ✔ в ваших вопросах
    Итак, вы сохраняете из админки, а это значит, что ваш сигнал (post_save надо, pre_save тут точно мимо) отрабатывает сразу после того, как будет сохранён объект User. Пока всё ок.

    Но именно с админкой есть нюанс: после того, как сохранится основная модель и выполнятся соответствующие этому действию сигналы, начнётся сохранение m2m связей, которые 100% у вас есть на странице админки, и перезапишут те данные, которые вы миллисекундами ранее сохранили с помощью сигнала.

    У меня была когда-то похожая проблема, и если мне не изменяет память, я создавал свой сигнал, который вызывал из переопределённого метода класса админки модели после сохранения вообще всего, а в обработчике своего кастомного сигнала уже спокойно работал с m2m-связями без этих проблем.

    Также, можно поиграться с имеющимся в Django сигналом m2m_changed, но это будет костыль, потому что этот сигнал будет вызван 2N раз, где N - число добавлений/удалений из m2m.
    Ответ написан
  • Почему здесь используется базовое исключение Exception?

    Assargin
    @Assargin
    Перед ответом смотрю наличие ✔ в ваших вопросах
    Это не безусловный антипаттерн. Если вы в блоке except логируете или перевыбрасываете исключение (как в вашем примере) - то норм, даже линтер не ругнëтся.
    Ответ написан
    Комментировать
  • Архитектура связей между таблицами в БД?

    Assargin
    @Assargin
    Перед ответом смотрю наличие ✔ в ваших вопросах
    events:
      id (PK)
      ....
    
    users:
      id (PK)
      ...
    
    event_users
      event_id (PK, FK to events)
      user_id (PK, FK to users)
    
    feedbacks:
      id (PK)
      event_id (FK to events)
      user_id_from (FK to users)
      user_id_to (FK to users)
      
      idx_unique (event_id, user_id_from, user_id_to) - уникальный индекс


    В event_users каждый пользователь на каждое событие может быть записан только 1 раз (т.к. оба поля входят в PK)
    В feedbacks на уровне приложения позволяете добавлять только те записи, где и user_id_from и user_id_to присутствуют в event_users к данному event.
    Ответ написан
    Комментировать
  • Как безопасно выгрузить данные из pickle-файла, модифицировать их, и обратно сдампить в Pickle?

    Assargin
    @Assargin
    Перед ответом смотрю наличие ✔ в ваших вопросах
    Давайте начнём с того, что откроем для себя, как пикл сериализует классы:
    >>> class MyKnowHowClass:
    ...   SECRET = '42'
    ...   TOP_SECRET = 'Kennedi was killed by UFO'
    ... 
    >>> import pickle
    >>> pickle.dumps(MyKnowHowClass)
    'c__main__\nMyKnowHowClass\np0\n.'
    >>>


    То есть, это всего лишь "указатель", описание того, откуда брать структуру данных. Если пикл не найдёт класс там, где указано - будет ЕГГОГ.
    Встроенные типы - ок. Наполняйте встроенные типы (списки, сеты, словари и т.п.) такими же встроенными типами (то же самое + скаляры) - и будет вам автономный пикл.
    А с пользовательскими классами - не-а.

    UPD:
    Если у вас экосистема, и тем более большая, и тем более уже выдумали "формат" - наверняка уже должны быть решены многие вопросы. Например, вопросы безопасности (ваш п.4), вопросы о загрузке-выгрузке (п.3), да и много чего можно посмотреть существующего и работающего на тему лучших практик по остальным двум пунктам.
    Ответ написан
  • Как сделать, чтобы функция начала работать с итераторами?

    Assargin
    @Assargin
    Перед ответом смотрю наличие ✔ в ваших вопросах
    Не надо кушать лишней памяти, и лишний раз ходить по итератору тоже.
    def all_unique(iterator):
        counter = 0
        items = set()
        for item in iterator:
            items.add(item)
            counter += 1
            if len(items) != counter:
                return False
        return True

    Худший случай, конечно, всегда один для данной задачи: О(N) и по памяти, и по времени. Но в лучшем случае может хватить всего лишь 2 итерации цикла и множества из 1 элемента (это когда в итераторе 2 первых элемента уже одинаковые), а в среднем будет что-нибудь типа O(N/2).
    Ответ написан
    Комментировать
  • Как выполнять комманду exec (docker-compose), чтобы файлы созданные впоследствие нее пренадлежали текущему пользователю на хост машине?

    Assargin
    @Assargin
    Перед ответом смотрю наличие ✔ в ваших вопросах
    Коль вы не даёте конкретно запускаемую вами команду и содержимое конкретно ваших docker/compose-файлов, то вот вам соответствующий вопросу ответ: у вас где-то ошибка, потому что работать должно.
    user@user-workstation:/tmp/test$ docker container run --rm -v ${PWD}:/tmp -u $(id -u ${USER}):$(id -g ${USER}) ubuntu:18.04 touch /tmp/it.is.my.file
    user@user-workstation:/tmp/test$ ll
    итого 20
    drwxrwxr-x  2 user user   4096 янв 22 13:55 ./
    drwxrwxrwt 23 root root  16384 янв 22 13:55 ../
    -rw-r--r--  1 user user      0 янв 22 13:55 it.is.my.file
    user@user-workstation:/tmp/test$ rm it.is.my.file 
    user@user-workstation:/tmp/test$ docker container run --rm -v ${PWD}:/tmp ubuntu:18.04 touch /tmp/it.is.my.file
    user@user-workstation:/tmp/test$ ll
    итого 20
    drwxrwxr-x  2 user  user   4096 янв 22 14:01 ./
    drwxrwxrwt 23 root  root  16384 янв 22 14:01 ../
    -rw-r--r--  1 root  root      0 янв 22 14:01 it.is.my.file
    user@user-workstation:/tmp/test$
    Ответ написан
    1 комментарий
  • В чём отличие @Decorator от @Decorator()?

    Assargin
    @Assargin
    Перед ответом смотрю наличие ✔ в ваших вопросах
    Декоратор - это функция, которой на вход подаётся функция и вернуть он должен тоже функцию. Например:
    @decorator 
    def foo():
     ..

    Эквивалентно foo = decorator(foo)

    Теперь про случай из вопроса. В качестве декоратора вместо обычной функции вы используете класс (точнее его объект):
    @FuncDec()
    def foo():
        print('Hello')

    Эквивалентно foo = FuncDec()(foo), а именно:
    • создаётся объект FuncDec (вызывается конструктор __init__)
    • этот объект вызывается (__call__) вместе с параметром foo
    • объект вернул функцию wrapper, которая и будет в дальнейшем выполняться вместо объявленной foo
    • профит

    Но если убрать скобки, то получится эквивалент foo = FuncDec(foo)
    То есть просто вызов конструктора, который у вас не принимает параметров - поэтому и ругается.
    Ответ написан
    Комментировать
  • Как решить ошибку Identation error?

    Assargin
    @Assargin
    Перед ответом смотрю наличие ✔ в ваших вопросах
    Это все из-за кнопки "Хакнуть Пентагон" (Да, я успел увидеть, потом вы отредактировали текст вопроса).

    Видите ли, в питоне встроена защита от причинения любого ущерба национальной безопасности США. Если интерпретатор при анализе текста программы видит, что вы собираетесь сделать что-то непоправимое, он выбрасывает ошибку и отказывается выполнять такую программу. Спецы говорят, обойти эту защиту вряд ли получится.
    Ответ написан
    1 комментарий
  • Как вывести пост, если пользователь отправивший запрос его владелец и 403 в противном случае django rest framework?

    Assargin
    @Assargin
    Перед ответом смотрю наличие ✔ в ваших вопросах
    После просмотра вашего кода смутило, что вы из django_user пытаетесь ещё обратиться к user. Так что чисто навскидку может помочь:
    if not obj.user == django_user:
        return Response(status=HTTP_403_FORBIDDEN)

    Но вообще в DRF есть permissions, и вот как раз второй пример из документации о владельце запрашиваемого объекта

    UPD: ой ёй, только щас увидел, какое у вас странное условие, вот так как-то правильнее будет
    if obj.user != django_user:
            return Response(status=HTTP_403_FORBIDDEN)
    Ответ написан
  • Как в Django сделать механизм, при котором через объединённые формы можно изменять значений у нескольких записей сразу?

    Assargin
    @Assargin
    Перед ответом смотрю наличие ✔ в ваших вопросах
    Если подходит вариант реализовать это в обычной админке Django, то решением может быть ModelAdmin.list_editable. Этот атрибут в джанге есть давно. И когда-то я его даже использовал.

    Есть 1 подводный камень, по крайней мере года 3 назад он точно был: если в админке одной и той же модели изменяли данные более 1 человека, то данные могли непредсказуемо потеряться и/или измениться.

    Поковыряв тогда исходники, я выяснил, что это происходит из-за того, что при сохранении отредактированных данных джанга опирается на относительное положение объекта (!) в кверисете (!!) в текущий момент!!!
    Т.е., популярно выражаясь: не "сохрани значение Super Item в поле title инстанса id=345 модели MyModel", а "сохрани значение Super Item в поле title инстанса, который в текущем кверисете MyModel.objects.filter(category="Items") на странице №2 находится на 14-й позиции".

    Причём сначала может показаться логичным, мол, классический кейс - одни и те же данные изменяют 2 и более человека, конечно можно получить перезаписывание, но если бы! Даже просто добавив новый объект этой модели, причём даже не из админки, можно запросто нарушить вот эту вот относительную "адресацию".
    Ответ написан
  • Python GIF to APNG как?

    Assargin
    @Assargin
    Перед ответом смотрю наличие ✔ в ваших вопросах
    возможн ли как то напрямую через сам PIL сконвертировать gif в apng?

    Что значит "напрямую"? Что вы в данном контексте вкладываете в это слово?
    Вы форматы GIF и APNG знаете? Я лично нет, но по ощущениям работы с инструментами по созданию анимированных гифок в лихих нулевых - в GIF-файле банально куча картинок-кадров + задан промежуток смены "кадров". Никаких, боже упаси, "потоков" и кей-фреймов, как в нормальных видеоформатах.

    Чем не понравилась либа apng? Я посмотрел - она похожа на живую, а pillow указана как опциональная зависимость (без неё возможна работа только с PNG).

    Однако сам то pil вполне себе умеет вынимать кадры... в общем кривые костыли

    Да никаких костылей. Если вы знаете, как извлечь все кадры из гифки, то у вас задача уже решена, по факту. Написать надо строк 10-15 кода, и всего делов.

    Любой конвертер из одного формата в другой будет оперировать кадрами, никаких тут "напрямую" и вообще ничего зазорного - нет.
    Ответ написан
    Комментировать
  • Почему код не работает(простенький калькулятор)?

    Assargin
    @Assargin
    Перед ответом смотрю наличие ✔ в ваших вопросах
    while True:
      abc=input("+ или - ?\n")
      if abc in ["+", '-']:
        break
      else:
        continue
    Ответ написан
    1 комментарий
  • Не могу понять, почему python выкидывает исключение?

    Assargin
    @Assargin
    Перед ответом смотрю наличие ✔ в ваших вопросах
    Вы результат преобразования никуда не сохраняете.
    Вот так работает без ошибок:
    a = input()
    b = input()
    l = input()
    N = input()
    
    a = int(a)
    b = int(b)
    l = int(l)
    N = int(N)
    
    lenl = 2 * l
    lena = (2 * N - 1) * a
    lenb = 2*b *(N - 1)
    sumlab = lenl + lena + lenb
    print(sumlab)


    или можно преобразовать сразу там же, где происходит ввод:
    a = int(input())
    b = int(input())
    l = int(input())
    N = int(input())
    
    lenl = 2 * l
    lena = (2 * N - 1) * a
    lenb = 2*b *(N - 1)
    sumlab = lenl + lena + lenb
    print(sumlab)
    Ответ написан
    2 комментария
  • Как добавить строчку записи в конец файла /etc/hosts в докерфайле?

    Assargin
    @Assargin
    Перед ответом смотрю наличие ✔ в ваших вопросах
    Цель задачи: необходимо, чтобы вместо "127.0.0.1:8080" можно набить в адресной строке http://test, те http://127.0.0.1:8080=http://test

    Чтобы решить эту задачу, вам надо сделать 2 вещи:
    1) руками в hosts-файл на вашей хост-машине добавить строчку 127.0.0.1 test. Так вы решите вопрос с именем хоста (test вместо IP 127.0.0.1)
    2) при старте контейнера с веб-сервером биндить порт веб-сервера из контейнера с 80-м портом вашей хост-машины (с помощью параметра --publish): что-то типа docker run --publish 80:8080 ... - здесь как бы написано: "связать внутренний порт контейнера 8080 с внешним портом хост-машины 80" (в вашем случае вроде как оба порта будут 80, и внутренний, и внешний).
    Если внешний порт, с помощью которого вы хотите смотреть веб-сайт, отличается от дефолтного (80 для HTTP и/или 443 для HTTPS) - никакими прописываниями ни в каких магических файлах не получится избавиться от указания порта при набирании адреса в адресной строке браузера.
    И ещё - если у вас на хост-машине есть что-то, что уже слушает 80-й порт - ну мало ли, может nginx какой запущен или тот же апач - то связать порты не выйдет, нужно сначала остановить тот сервис, что сидит на 80-м порту. Ну или не париться и использовать любой другой порт.

    В плане именно манипуляций с докером: кроме бинда внутреннего порта контейнера на "внешний" вашей хост-машины ничего больше не нужно делать. Контейнер вам в файл хост-машины строчку не пропишет. Не, технически это возможно сделать, но не нужно совершенно точно.

    И скажите: откуда вообще изначально взялась такая потребность? Если чисто сделать красивый адрес для использования локально в процессе разработки - ну ок, хотя можно и без этого. Если вы делаете это уже для продакшна - то всё, что вы описали в вопросе - не то, не так и не в ту сторону.
    Ответ написан
    1 комментарий