1. Python / Говнокод #24306

    +1

    1. 1
    2. 2
    In [42]: os.path.join(r'c:\asd', r'c:\www')
    Out[42]: 'c:\\www'

    Нахуя???

    Запостил: syoma, 23 Мая 2018

    Комментарии (118) RSS

    • If a component is an absolute path, all previous components are thrown away and joining continues from the absolute path component.

      Всё очень секурно. Думаешь защититься от обхода каталога? А вот хер тебе об твой лысый череп?

      Как сделать чтобы os.path.join(path1, path2) не выходил за пределы path1? Ваши версии?
      Ответить
    • показать все, что скрытоvanished
      Ответить
      • Такие функции как раз и нужны, чтобы обработать всякую нетривиальную питушню корректно. Конкатенацию хороших строк и наклон дроби в другую сторону в Питонии можно и вручную сделать в одну строку.
        Ответить
        • показать все, что скрытоvanished
          Ответить
          • > И то, и то - одинаково логично.
            Согласен.
            Ответить
          • Ожидалось, что оно поможет защитить от обхода каталога. А тут вообще радость - сразу можно абсолютные пути юзать.
            Ответить
            • > поможет защитить
              Как минимум от ".." оно не защищает.
              Ответить
              • Хотя бы от использования абсолютных путей
                Ответить
                • Нужна защита от пользовательского ввода — проверяй как положено, а не надейся, что низкоуровневые функции за тебя это сделают сами.

                  З.Ы. Поведение документировано.
                  Ответить
                  • Ну как сказать пользовательского. Скорее, програмистского, но опечатавшись насрать в корень не хочется. Но это не то же самое что проверка на веб серваке, да.

                    > З.Ы. Поведение документировано.
                    А нахер оно нужно? Как же принцип наименьшего удивления? Какая логика за ним стоит? Или поставили знак "битая дорога" - и можно не ремонтировать?
                    Ответить
                    • > А нахер оно нужно?
                      Как я понял, ради простого использования в конфигах и командной строке, чтобы isabs() не дёргать…
                      os.path.join("/some/default/dir", path_from_config)
                      path_from_config = "foo/bar.txt"  # /some/default/dir/foo/bar.txt
                      path_from_config = "/foo/bar.txt" # /foo/bar.txt
                      Других причин не могу придумать.
                      Ответить
                      • Охереть, и это местечковое поведение они протащили в стандартную библиотеку?
                        Ответить
                        • Х.з., гвидо же вроде лютый сишник. А питон изначально никто и не думал юзать в больших серьёзных проектах… Скорее всего для своих первых скриптов запилил, а потом убирать уже было поздно.
                          Ответить
                          • Была же тройка, где дохера всего перелопатили, включая байтовые строки, из-за чего поломалось дохера софта.
                            Ответить
                          • Кстати, почему всё говно таскают для совместимости долгие годы и не хотят менять?
                            Почему нельзя сделать что-то вида
                            #include <libcversion.h>
                            
                            #ifdef LIBC_V3
                            int Create(const std::string& pathname, mode_t mode);
                            #endif
                            
                            #ifdef LIBC_V2
                            int create(const char *pathname, mode_t mode);
                            #endif
                            
                            #ifdef LIBC_V1
                            int creat(const char *pathname, mode_t mode);
                            #endif

                            А в libcversion.h - что-то вида
                            #if defined (LIBC_LATEST) || defined (LIBC_V3)
                            #  ifndef LIBC_V3
                            #  define LIBC_V3
                            #  endif
                            #elif defined (LIBC_V2)
                            #  ifndef LIBC_V2
                            #  define LIBC_V2
                            #  endif
                            #else
                            #  ifndef LIBC_V1
                            #  define LIBC_V1
                            #  endif
                            #endif

                            Выбираешь себе версию - и вперёд. Страж включения, конечно, придётся сделать более тонким (по стражу на версию, чтоб в разных частях включать разные версии), но это реализуемо.
                            Аналогично - в Питонии, только там даже проще реализовать.
                            Ответить
                            • Как это должно работать на питоне, пример?
                              Ответить
                              • import os as os_old
                                import os:2 as os2
                                import os:latest
                                
                                os.path.join(...) # секьюрно, но совместимость может пострадать
                                os2.path.join(...) # секьюрно и будет работать, пока жив python
                                os_old.path.join(...) # как в старых конфигах у Гвидо
                                Ответить
                                • > os:2
                                  Ну это, по сути, просто новая либа. А старую задепрекейтили и когда-нибудь выпилят. Можно было просто написать os2 с тем же успехом.

                                  > os:latest
                                  Для любителей мазохизма и поломанных билдов? :)
                                  Ответить
                                  • > А старую задепрекейтили и когда-нибудь выпилят.
                                    И потом программа не запускается только потому, что в 2030 году кто-то из соображений перфекционизма в сигнатурах решил выбросить код, стабильно работавший в 2010 и 2020.
                                    Надо хотя бы в репозитории оставить старую библиотеку, не включая в стандартную поставку, чтобы старую прогу можно было запустить в любое время просто после вызова какого-нибудь pip resolve program.py.

                                    >> os:latest
                                    > Для любителей мазохизма и поломанных билдов? :)
                                    Для обычных людей? Сейчас, в общем-то, обычные люди так и живут с os:latest. Разве что, такое версионирование развялало бы руки посильнее.
                                    Ответить
                                • > os_old
                                  На которую тут же забьют болт и даже баги не будут править, как во многих модулях двойки? Да что там модулях, там даже сырые строки не сырые.
                                  Ответить
                                  • И пусть забивают. С ней десять-двадцать лет работало, и будет работать дальше.
                                    Править её надо только если где-то внизу (в ОС, например) что-то поменялось так, что без правок код os_old уже не работает.
                                    Для нового кода - новая версия без багов. Для старого - возможность запускаться как раньше без потребности каждую версию питона отмечать проверками совместимости.
                                    Ответить
                            • Ага, и тебе в либе всё равно все 3 версии поддерживать придётся т.к. ты не знаешь какой клиент будет её юзать. Поэтому обычно просто помечают старые функции как deprecated и через пару лет выпиливают их со злобным смехом. Ну либо запиливают новую либу, несовместимую с предыдущей и какое-то время поддерживают обе.

                              К слову, в libc были такие изменения. К примеру, когда завезли потоки и errno превратилось в макрос. Куча старого софта, где errno любили юзать напрямую, тупо перестала запускаться.
                              Ответить
                              • > тебе в либе всё равно все 3 версии поддерживать придётся т.к. ты не знаешь какой клиент будет её юзать.
                                Но так это придётся поддерживать только в стандартной библиотеке. Интерфейсы обычных библиотек должны быть независимыми от версии стандартной (можно один раз выбрать LIBC_V2 и на ней писать).

                                Но стандартная библиотека не должна поддерживать старые версии, они просто останутся в коде. Изменять их опасно, т.к. на них полагаются. Тут можно даже баги документировать. Изменяться будет только последняя версия, пока поверх неё не появится новое наложение.
                                Ответить
                                • > интерфейсы должны быть независимыми от версии стандартной
                                  Ну т.е. в твоей стандартной либе не будет даже банальных string и vector? Только тоненькая обёртка над ядерным API (которое обязано быть обратно-совместимым иначе вся твоя идея с запуском старых прог развалится), только хардкор?
                                  Ответить
                                  • Почему, пусть будут string и vector. (Впрочем, ничего не мешает убрать string и vector в полустандартную библиотеку вроде Boost, если пакетный менеджер сможет в любое время в один клик доставить нужный кусок Boost нужной версии.)

                                    В стандартной библиотеке будет несколько ревизий нужных функций, поддерживаться и изменяться серьёзно будет только последняя, старые будут только сохранять их контракт и свою работоспособность. В некоторых случаях старые версии могут быть переписаны через новые (если API ОС вдруг сильно изменилось и т.п., когда новая функция более общая), когда это проще для сохранения их работоспособности.

                                    >> интерфейсы должны быть независимыми от версии стандартной
                                    Это про интерфейсы обычных библиотек, которые используют стандартную.
                                    Библиотека Васи Пупкина должна работать согласно её документации, пользователь должен заботиться только о её версии, а не о версиях использованных ей библиотек.
                                    Ответить
                                    • И всем придётся заворачивать string и vector в свои (т.к. стандартные выставить нельзя). Но программисты разумные и они сделают свою либу со string'ом и все будут её юзать. А стандартный останется нахуй никому не нужным. Т.е. его можно было изначально туда не добавлять.

                                      Если стандартная либа не будет юзаться как базис для связи либ и прог между собой — нет смысла делать что-то сложнее тоненькой обёртки над ядром.
                                      Ответить
                                      • А, понял, о чём речь. С типами данных плохо выходит.
                                        Тут либо надо запрещать использовать те же имена для разных версий, либо в новых версиях только добавлять поля в структуры.
                                        Возможно, стандартные конвертеры тоже придётся запиливать.

                                        Библиотеки можно тогда привязывать к конкретным версиям стандартной библиотеки, описывая это чётко в документации, чтобы пользователь знал, какие точно типы данных там будут и сильнее этого с версиями не возился. (можно реализовать и многоверсионный вариант просто естественным образом переходя на новую версию стандартной библиотеки и замораживая разработку под прошлую версию - тут с учётом фиксированности старых версий стандартной библиотеки поддерживать вообще ничего не надо, если контракт соблюдали)
                                        Ответить
                          • > лютый сишник
                            Сишного говна в сетевой библиотеке более чем дохера. Все эти кальки с сишного api в socket, когда даже в перле любой сокет (клиентский, серверный) со всеми параметрами создавался одним конструктором.
                            Ответить
                            • Ну там же есть create_connection, или его не сразу завезли?
                              Ответить
                              • https://perldoc.perl.org/IO/Socket/INET.html

                                Любой сокет одним вызовом. А в питоне как? И кроме socket() я еще нигде ничего не видел.
                                Ответить
                                • create_connection
                                  Ответить
                                  • > Любой
                                    Слушающий тоже? Почитай по ссылке.

                                    Не видел в реальном коде.
                                    Ответить
                                    • > Не видел в реальном коде.
                                      Потому что реальный код писали сишники, которых socket() не напрягает?
                                      Ответить
                                      • Напитоне пишут одни сишники?
                                        Ответить
                                        • > одни сишники
                                          А остальные юзают высокоуровневые протоколы, реализованные сишниками (http и т.п.)
                                          Ответить
                                          • Доо, сокеты юзают одни дебилы и хакеры, на все протоколы есть модули.
                                            Ответить
                            • Ну а SocketServer тебе чем не нравится?
                              Ответить
                              • Это где?
                                Ответить
                                • https://docs.python.org/2/library/socketserver.html
                                  Ответить
                                  • А SocketClient где?
                                    Ответить
                                    • А он не нужен, клиенту хватит create_connection.
                                      Ответить
                                      • Ты быстренько поправил и думаешь что никто не заметит?
                                        >Ну а SocketServer и SocketClient тебе чем не нравятся?

                                        А create_connection не видно за горой хлама.
                                        Ответить
                                        • > Ты быстренько поправил и думаешь что никто не заметит?
                                          Кстати, некто Борманд недавно запилил на своём сервере штуку, которая пишет все доступные ей версии комментариев.

                                          Борманд, берегись, большой Борманд следит за тобой!
                                          Ответить
                                        • > не видно за горой хлама
                                          Вот за это я и ненавижу доки питона.

                                          К слову о сокетах — какого хуя там есть sendall (отправить буфер целиком), но нету recvall (вернуть ровно N байт или кинуть исключение при EOF)?
                                          Ответить
                                          • Причем тут доки? Я про остальные функции модуля.

                                            Доки самого питона неплохие, но напрягает что они недоступны из ide/консоли. А вот в сторонних либах уже как повезёт.

                                            А в c recvall есть? Что он должен делать? Вообще, всё это нахуй не нужно. makefile() и дальше работаешь как с файлом. В жавке так и сделано (FilteredStream, кажется), можешь считать из потока int и вызов заблокируется, пока в сокет не придут данные в нужном объеме.
                                            Ответить
                                            • > и дальше работаешь как с файлом
                                              Хм, спасибо, попробую.

                                              > Что он должен делать?
                                              Вот это: вызов заблокируется, пока в сокет не придут данные в нужном объеме.

                                              > А в c recvall есть
                                              Так там и sendall нету. Вот эта неконсистентность и бесит. Зачем делать один хелпер, но не делать второй?
                                              Ответить
                                              • В каком объеме? socket.recvall(length), возвращает управление когда пришло length байт? Да, нужная вещь, согласен.
                                                Ответить
                                                • > socket.recvall(length)?
                                                  Да, тип того.
                                                  Ответить
                                                  • Попробуй makefile().read(length), интересно, он будет ждать length байтов?
                                                    Ответить
                                                    • > будет ждать length байтов
                                                      Да должен, по идее, судя по доке он просто читает в цикле.

                                                      З.Ы. Ждёт, всё ок. Спасибо.
                                                      Ответить
                                                      • Доке на file.read()?

                                                        >З.Ы. Ждёт, всё ок. Спасибо.
                                                        Пожалуйста :)
                                                        Ответить
                                                        • > file.read
                                                          Да.
                                                          Ответить
                                                          • А что будет делать sock.makefile().read()? Блокировать и читать пока там сокет не закроют?
                                                            Ответить
                                                            • > Блокировать и читать пока там сокет не закроют?
                                                              Да, так и работает.
                                                              Ответить
                                          • > вернуть ровно N байт или кинуть исключение при EOF
                                            А как себя read() поведёт? Вернёт меньше байтов? Жавка хоть исключение бросала.
                                            Ответить
                      • Докстринг: Join two or more pathname components, inserting "\" as needed. Никакого упоминания даже нет. А в тройке даже докстринга нет. После жавы, где никакой документации на апи отдельной от той, что в сорцах, у меня от этого боль.
                        Ответить
                        • Да в питоне доки какое-то говно, если честно. Портянка-туториал которую надо полностью читать вместо нормального референса.
                          Ответить
                          • +1. Апидока по факту просто нет. А в референсе доки на нужные функции могут быть, а могут и не быть. С апидоками хоть в сырцах сразу видно, где документировали, а где хуй забили.

                            Только если сказать это питонисту - он глазёнки выпучит и побежит на тебя в штыковую. За что я питонокомунити и не люблю (привет, питон.ссу!). Даже пыхари здесь имхо адекватнее.
                            Ответить
                          • зато по прочтении ты реально понимаешь как пользоваться функцией, а вот в джаве с точностью наоборот: референс есть, а туториал отдельно

                            Что толку знать что парметр foo типа bar это anchor если ты не знаешь что такое anchor?

                            Кроме того в питоне тяжело делать референс потому что может быть 28 видов вызова функции с разными kwargs


                            Мне больше всего нравится (внезапно!) MSDN по win32api: там и Remarks очень мощный и референс подобный

                            зацени
                            https://msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85).aspx


                            маны у никсов еще иногда бывают ничего (ну кроме опендесктопа конечно)
                            Ответить
                            • А у anchor какой тип? Да, туториалы могут быть полезны, но сука, мне хочется иметь доки на апи. Их не просто так придумали.

                              > win32api
                              Низкоуровневое говно.
                              Ответить
                            • >Кроме того в питоне тяжело делать референс потому что может быть 28 видов вызова функции с разными kwargs
                              А потом ты видишь код, работающий не так как надо, сидишь и смотришь на него как баран на новые ворота.
                              Ответить
                            • > 28 видов вызова функции с разными kwargs
                              Во-первых, можно по каждому аргументу написать, что туда написать.
                              Во-вторых, 28 непересекающихся наборов - это уже говнокод, а 5-6 вполне можно оформить как перегрузки.
                              И сделать конфетку как на cplusplus.com
                              Ответить
                  • > проверяй как положено, а не надейся, что низкоуровневые функции за тебя это сделают сами
                    Но для того, чтобы проверять как положено, должны быть специальные библиотечные функции, которые учитывают всё, что можно, прошли месяцы/годы проверок в реальных ситуациях, аудит или хотя бы множественное прочтение сообществом.
                    Тупые функции, которые ничего не проверяют, можно и самому написать. А секьюрную питушню надо в стандартную библиотеку и на каменных плитах выдолбить, чтоб своими велосипедами багов не добавляли.
                    Ответить
                    • Тем более, то, что работает на прыщах (абсолютный путь начинается с /), не работает на винде (он может начинаться с буквы диска). Это я даже про слеши не говорю. Собсно, для этого os.path и придумали.
                      Ответить
      • Да??? Про пользовательский ввод не слышал?
        Ответить
    • > Нахуя???
      Такое поведение тоже нужно в некоторых случаях. Только недавно сталкивался: надо было взять путь, и если он был не абсолютный, добавить к нему каталог по умолчанию.
      Авторам надо было реализовать оба варианта, т.к. оба на практике нужны.

      Я бы порекомендовал бы зафильтровать пользовательскую питушню максимально до предела по регулярке, чтоб туда проникли только латинские символы, цифры, дефис и т.п., а точка могла бы быть только как расширение - между двумя непустыми наборами вышеупомянутых символов.
      Ответить
      • Только сделали нормально работающий юникод, а ты, сука, сразу хочешь его порезать?
        Ответить
        • А зачем разрешать юникод там, где пользователь ввёл какую-нибудь очередную чушь?

          Тем более, если это вэб-сервер. Хрен знает, что там будет на выходе - старшие байты обрежутся, символ после приведения к инварианту станет точкой, кто-нибудь засунет полусимвол, который вызовет исключение в декодере или ещё какая питушня произойдёт. JSFuck смог пролезть через 6 символов, а тут тысячи их. Тем более, на вэб-сервере это перейдёт в безумные xn--p1ai или в %A1%D0. Ну и мировому сообществу кириллица и иероглифы нафиг не нужны, особенно если ссылку надо где-то напечатать.
          Ответить
          • Это не вэб-сервер. С чего ты это взял?
            Ответить
            • Не знаю, просто секьюрность для путей обычно нужна именно в случае, когда есть сервер и клиент, которому нельзя доверять. Для обычного пользователя можно обойтись настроенными правами; для домашнего пользователя - принципом "удалил c:\ - сам виноват".
              Ответить
    • Ох ебать, os.path.join("c:", "foo") выдаст c:foo, которое не эквивалентно c:\foo…
      Ответить
    • https://bugs.python.org/issue28488

      Питон на винде до 2016 года архивы нормально создавать не умел.
      Ответить

    Добавить комментарий