Форум русскоязычного сообщества Ubuntu


Следите за новостями русскоязычного сообщества Ubuntu в Twitter-ленте @ubuntu_ru_loco

Автор Тема: Клиент синхронизации с Yandex.disk (Python3, OpenSource)  (Прочитано 31105 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Punko

  • Гость
Re: Клиент синхронизации с Yandex.disk (Python3, OpenSource)
« Ответ #15 : 13 Октября 2016, 11:31:46 »
Sly_tom_cat, а я не совсем понимаю, как мне хранить полученный токен.
При выполнении программы он каждый раз получается. Это надо где-то в файл его прописывать, как думаешь? Ну, чтоб БД не городить ради пары записей.

Какой-то файл конфигурации, который будет содержать еще и полученный токен.

Оффлайн Sly_tom_cat

  • Автор темы
  • Don't worry, be happy!
  • Заслуженный пользователь
  • Старожил
  • *
  • Сообщений: 12130
  • Xubuntu 22.04
    • Просмотр профиля
    • Github
Re: Клиент синхронизации с Yandex.disk (Python3, OpenSource)
« Ответ #16 : 13 Октября 2016, 13:19:36 »
Посмотри у меня там в репе простенький объект конфигурации: https://github.com/slytomcat/yandex-disk-client/blob/master/jconfig.py.

Он очень простой - просто объявляешь конфиг как
config = Config(<путь к файлу конфигурации>)config будет словарем (dict).

Хочешь из него прочитать значение: val = config[key]

Если опасаешься, что значения в файле нет, то читаешь так: val = config.setdefault(key, default) - получаешь значение по умолчанию если значения не было, а не ошибку (которую получишь при config[key] если key нет в соловаре). setdefault одновременно и в словарь записывает значение по умолчанию.

Записываешь в конфиг данные как в словарь: config[key] = val.

Если хочешь записать чехом несколько значений (они должны быть в словаре), то выполняешь config.append(dictValue)

Это программная сторона конфига, а вся файловая его ипостась реализуется через config.load() (выполняется на автомате при создании конфига, если не передашь в конструктор доп параметр load=False) и config.save(). Оба этих метода возвращают True/False в зависимости от того удалась операция или нет. в файле словарь конфигурации сохраняется в JSON формате (вполне читаем и можно подредактировать соблюдая осторожность).

Для удобства есть еще пара свойств:
config.loaded - устанавливается в True после успешного чтения из файла (его полезно просмотреть после вызова конструктора что бы понять прочитался конфиг или нет).
config.changed - его можно самостоятельно ставить при изменении конфига как словаря + он устанавливается в True после config.append. Сбрасывается этот флаг после успешной записи в файл (config.save()). Мой опыт подсказывает, что иметь такую флаг очень удобно - правишь настройки через диалог - там может что-то поменялось, а может нет - х.з. А тут прямо у конфигурации есть метка, поменял в нем значение - поставь ее в True, а записать в конфиг имеет смысл на выходе из диалога, один раз, а не каждый раз при изменении отдельных значений.

Примеры использования можно посмотреть самом файле (там снизу приписаны авто-тесты) или в Disk.py.

В принципе класс вполне отработан, но он довольно немой - если в файле будет нарушение JSON структуры - он тупо не читает файл и ничего не говорит. Поэтому лучше файл руками не трогать без особой нужды.
« Последнее редактирование: 13 Октября 2016, 16:16:51 от Sly_tom_cat »
Индикатор для Yandex-Disk: https://forum.ubuntu.ru/index.php?topic=241992
UEFI-Boot - грузимся без загрузчика: https://help.ubuntu.ru/wiki/uefiboot

Оффлайн Sly_tom_cat

  • Автор темы
  • Don't worry, be happy!
  • Заслуженный пользователь
  • Старожил
  • *
  • Сообщений: 12130
  • Xubuntu 22.04
    • Просмотр профиля
    • Github
Re: Клиент синхронизации с Yandex.disk (Python3, OpenSource)
« Ответ #17 : 13 Октября 2016, 13:29:35 »
Идея с токеном - тривиальная: при запуске приложения читаешь конфиг приложения, и если в нем нет токена - запускаешь получение, сохраняешь его в конфиге и записываешь конфиг на диск. При последующих запусках токен будет браться из конфигурационного файла.

Я в дисковом клиенте изначально запиливаю множественные акаунты к YD. т.е. у меня в конфиге хранится значение disks, которое само по себе - словарь с отдельными акаунтами, а каждый аканут, в свою очередь - это словарь с настройками пользователя. Токен я запихиваю в настройки пользователя. Т.е. структура примерно такая:
            {'type': 'std',
             'disks': {'login1': {'path': '~/ydd',
                                  'auth': '87816741346',
                                  'start': True,
                                  'exclude': ['tests', 'other/private'],
                                  'ro': False,
                                  'ow': False },
                       'login2': {'path': '~/yd',
                                  'auth': '84458090987',
                                  'start': True,
                                  'exclude': ['new'],
                                  'ro': True,
                                  'ow': False }
                      }
            }
« Последнее редактирование: 13 Октября 2016, 13:33:06 от Sly_tom_cat »
Индикатор для Yandex-Disk: https://forum.ubuntu.ru/index.php?topic=241992
UEFI-Boot - грузимся без загрузчика: https://help.ubuntu.ru/wiki/uefiboot

Punko

  • Гость
Re: Клиент синхронизации с Yandex.disk (Python3, OpenSource)
« Ответ #18 : 13 Октября 2016, 13:56:14 »
Sly_tom_cat, да, отлично, я пришёл к такому же выводу, спасибо!

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

Оффлайн Sly_tom_cat

  • Автор темы
  • Don't worry, be happy!
  • Заслуженный пользователь
  • Старожил
  • *
  • Сообщений: 12130
  • Xubuntu 22.04
    • Просмотр профиля
    • Github
Re: Клиент синхронизации с Yandex.disk (Python3, OpenSource)
« Ответ #19 : 13 Октября 2016, 14:34:57 »
Вчера ловил некоторые ограничения с модулем requests при передаче кириллицы
не забывай, что там у объекта requests есть свойство .text - в нем уже в правильной кодировке, Собственно, как я понимаю, то что отправляется тоже преобразуется т.к. в URL есть ограничения на не ASCII символы. Но я не проверял.
Индикатор для Yandex-Disk: https://forum.ubuntu.ru/index.php?topic=241992
UEFI-Boot - грузимся без загрузчика: https://help.ubuntu.ru/wiki/uefiboot

Оффлайн Sly_tom_cat

  • Автор темы
  • Don't worry, be happy!
  • Заслуженный пользователь
  • Старожил
  • *
  • Сообщений: 12130
  • Xubuntu 22.04
    • Просмотр профиля
    • Github
Re: Клиент синхронизации с Yandex.disk (Python3, OpenSource)
« Ответ #20 : 13 Октября 2016, 20:23:46 »
Допилил объект Cloud - это обертка для запросов через REST API яндексдиска.

Собственно теперь надо допилить полную сверку (нужно только собрать рекурсивно все файлы в локальной директории, все остальное - уже есть) и будет уже полу живой клиент: при запуске полная двухсторонняя синхронизация и все изменения отражаются на облачном диске.

Само собой это все еще сыро и требует отладки и вылизывания (там практически нет обработки ошибок).

Но из полного функционала (полная синхронизация, синхронизация локальных изменений в облако и синхронизация из  изменений в облаке на локальный диск) останется только последний кусок.... но он пока и самый непонятный....


Индикатор для Yandex-Disk: https://forum.ubuntu.ru/index.php?topic=241992
UEFI-Boot - грузимся без загрузчика: https://help.ubuntu.ru/wiki/uefiboot

Оффлайн Sly_tom_cat

  • Автор темы
  • Don't worry, be happy!
  • Заслуженный пользователь
  • Старожил
  • *
  • Сообщений: 12130
  • Xubuntu 22.04
    • Просмотр профиля
    • Github
Re: Клиент синхронизации с Yandex.disk (Python3, OpenSource)
« Ответ #21 : 14 Октября 2016, 21:01:25 »
Все-таки пришлось делать свой, доработанный PoolExecutor

Встроил в него нотификатор - метод (можно переопределить в супер-классе) который дергается каждый раз, когда PoolExecutor начинает работать (получает задачу на исполнение) или тогда, когда у него не остается больше задач (ни в очереди ни на исполнении). Параметром передается True при запуске и False при остановке.

Для этого правда пришлось подработать напильником и сам PoolExecutor.

Оказалось, что в Queue есть интересный механизм: каждый раз когда из очереди что-то забирают (через get()) в очереди увеличивается счетчик unfinished_tasks, а уменьшается он тогда, когда очереди рапортуют, что взятая задача обработана (через вызов метода очереди task_done()).

На этот счетчик повешена функция очереди join() она подвешивает вызвавший поток до тех пор пока очередь не опустеет и счетчик unfinished_tasks не обнулится.

Вот на этот механизм я и навесил контроль за окончанием активности PoolExecutor.

Ну а попоутно, вывесив счетчик незавершенных работ из очереди наружу (он равен числу элементов в очереди плюс unfinished_tasks) я еще доработал PoolExecutor в части создания новых рабочих потоков. Раньше оригинально он их создает каждый раз когда получает новую задачу в очередь на исполнение, пока не упрется в ограничение. Но смысла создавать новый поток, когда есть простаивающие - нет никакого. Поэтому я там ввел условие - если число уже созданных потоков меньше или равно числу незаконченных работ (в очереди и на исполнении) то новый поток не создается.

Погонял загрузку в облако на 20 рабочих потоках: 23 файла общим объемом 126,8 МБ - залились за 15.3 секунд.
Штатный клиент загрузил (по отчету) чуточку быстрее, однако там еще вечно запускается какая-то индексация (х.з. что этот статус значит) и вот вместе с ней получилось 23.7 секунды....

Пока есть непонятные глюки inotify - не понятно по каким причинам постоянно отваливается вотчинг внутри каталогов начиная с первого уровня..... хотя я явно задаю вотчить каталог рекурсивно..... надо еще копать....
Индикатор для Yandex-Disk: https://forum.ubuntu.ru/index.php?topic=241992
UEFI-Boot - грузимся без загрузчика: https://help.ubuntu.ru/wiki/uefiboot

Оффлайн Sly_tom_cat

  • Автор темы
  • Don't worry, be happy!
  • Заслуженный пользователь
  • Старожил
  • *
  • Сообщений: 12130
  • Xubuntu 22.04
    • Просмотр профиля
    • Github
Re: Клиент синхронизации с Yandex.disk (Python3, OpenSource)
« Ответ #22 : 17 Октября 2016, 18:19:31 »
По ходу доработок упростил проверку простоя PoolExecutor-а - просто вывел из него число незаконченных задач от очереди (то самое, что в очереди + на исполнении). В любой момент можно спросить PoolExecutor сколько у него незаконченных задач и понять - стоит он или трудится в поте лица своего. Избавился от необходимости создавать отдельный поток (вся задача которого висеть на join() очереди и когда очередь его отпустит - зарепортить простой экзекутора.

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

Сейчас в проекте достигнута первая ступень: Все изменения в локальном каталоге транслируются на диск. Но правда есть еще нюансы.... правильнее сказать "все неспешные или все простые...."

В чем суть проблемы:
Модель, при которой поток событий от файловой системы перелопачивается параллельно в независимых потоках дает заметное преимущество при однотипных операциях (заливка на диск пачки файлов, удаление пачки файлов), но дает почти 100% сбой при последовательных операциях с одним файлом.

К примеру набор команд выполненный в синхронизируемом каталоге:
touch 1/f2; mv 1/f2 .;rm f2а вот что в логе клиента:
IN_Event: IN_CREATE, path: 1/f2                            # поймали "touch 1/f2"
submit Cloud.upload of  ('/home/stc/yd/1/f2', '1/f2')      # закинули в очередь задачу #1 на создание файла 1/f2
IN_Event: IN_MOVED_FROM, path: 1/f2                        # поймали первую половину от "mv 1/f2 ." - откуда
IN_Event: IN_MOVED_TO, path: f2                            # поймали вторую половину от "mv 1/f2 ." - куда
submit Cloud.move of <C ('1/f2', 'f2')                     # поженили половинки и закинули в очередь задачу#2:
                                                           #   переместить 1/f2 в f2
IN_Event: IN_DELETE, path: f2                              # поймали "rm f2"
submit Cloud.delete of  ('f2',)                            # закинули в очередь задачу#3: удалить f2
Done: (False, '404 : del f2'), 2 unfinished                # быстрее всех обломилось удаление (задача#3) - нечего удалять
Done: (False, '404 : move 1/f2 to f2'), 1 unfinished       # затем упало перемещение (задачу#2) нет еще на
                                                           #    диске файла 1/f2 т.к. задача #1 еще не завершилась.
Done: (False, 'FileNotFoundError : up 1/f2'), 0 unfinished # ну и наконец упала задача#1: она не нашла в локальном
                                                           #    каталоге 1/f2 т.к. он был сразу же перемещен

Эпик фейл детектед  :2funny: :D

Причем ведь все правильно и логично: правильно отлавливаются и ставятся в очередь события, задачи правильно обрабатываются и... правильно падают  :2funny: 8)

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

Причем ведь даже последовательное выполнение задач (в одном потоке) точно так же сбойнет на рассмотренном примере. Ведь задача на upload нового файла не успеет закончится до того момента, когда он будет перемещен или удален.

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

Какие видятся решения:
А красивых решений собственно и нет (я по крайней мере - не вижу). Вижу только такие:
1. Можно очень тупо: по первому событию запустить таймер на заданный интервал, и продлевать таймаут пока не кончится поток событий. А когда поток событий закончится ... правильно - выкинуть все события (потому что тольку от них не слишком много) и сделать полную сверку облака с локальным диском.
2. Другой вариант - делать все по событиям забивая на ошибки, но если были ошибки, то по окончании потока событий запустить опять же - полную сверку.

Первый вариант хорош тем, что может в общую кучу отловить даже несколько последовательных обновлений одного файла и залить его в облако всего один раз. Но страдает тем, что пока с синхронизируемым каталогом интенсивно работают - то никакой синхронизации вовсе нет.   

Второй вариант хорош тем, что обеспечивает высокую оперативность заливки на диск потока простых (несвязанных) операций, и благодаря этому мы получим гораздо меньший объем, который придется дополнительно синхронизировать после полной сверки. Но вот последовательные изменения в небольшом файле могут заливаться на диск много раз (иногда падая с ошибками).

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

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

Получить такой же список с файловой системы - не вопрос (все просто сделать через стандартные питоновские библиотеки glob и/или pathlib в итоге сделал через os.walk ).

Осталось просто все это собрать в один алгоритм, желательно опять же не все чехом обрабатывать, а по пачкам.
« Последнее редактирование: 18 Октября 2016, 11:06:29 от Sly_tom_cat »
Индикатор для Yandex-Disk: https://forum.ubuntu.ru/index.php?topic=241992
UEFI-Boot - грузимся без загрузчика: https://help.ubuntu.ru/wiki/uefiboot

Punko

  • Гость
Re: Клиент синхронизации с Yandex.disk (Python3, OpenSource)
« Ответ #23 : 17 Октября 2016, 18:47:53 »
1. Можно очень тупо: по первому событию запустить таймер на заданный интервал, и продлевать таймаут пока не кончится поток событий. А когда поток событий закончится ... правильно - выкинуть все события (потому что тольку от них не слишком много) и сделать полную сверку облака с локальным диском.
я вот о чём-то таком думал, когда читал о проблеме, да.

Сегодня вечером потыкаю на Федоре 24 клиент.

Оффлайн Sly_tom_cat

  • Автор темы
  • Don't worry, be happy!
  • Заслуженный пользователь
  • Старожил
  • *
  • Сообщений: 12130
  • Xubuntu 22.04
    • Просмотр профиля
    • Github
Re: Клиент синхронизации с Yandex.disk (Python3, OpenSource)
« Ответ #24 : 18 Октября 2016, 10:56:03 »
Отладил вчера процедуру полной синхронизации..  :idiot2:
Удалось это сделать только полностью загасив iNotify вотчер. Там когда файлы скачиваются - такой немерянный поток событий порождается - на каждый файл по стопятьсот апдейтов - когда первый раз пусканул полную синхронизацию с работающим вотчером - такую фигню получил, что просто алес капут....

Вот теперь надо думать. С одной стороны можно собрать план полного обновления (отдельным списком, не отдавая на исполнение) и все файлы из этого списка добавить временно в исключения для вотчера. Но вся фигня в том, что с вотчером пока есть неразрешенная трабла - он не удаляет рекурсивно вотчи - глк там какой-то - багу завел, но там как-то не слишком активно автор прилит....:( А если вотчинг не убрать то, как бы ничего и не сделать.....

Одним словом все больше склоняюсь к варианту отказа от постановки задач по вотчеру.... но буду еще копать....
Индикатор для Yandex-Disk: https://forum.ubuntu.ru/index.php?topic=241992
UEFI-Boot - грузимся без загрузчика: https://help.ubuntu.ru/wiki/uefiboot

Punko

  • Гость
Re: Клиент синхронизации с Yandex.disk (Python3, OpenSource)
« Ответ #25 : 18 Октября 2016, 22:30:36 »
Sly_tom_cat, я скачал репку себе.
Два вопроса:
1. Почему нет шебанга?
2. Укажи минимальную версия для пайтона.

При первом запуске Disk.py вываливается с ошибкой:

FileNotFoundError: [Errno 2] No such file or directory: 'OAuth.info'
Что это за файл и где он должен создаваться?
Сейчас на работе завал с пайтоном, за день от него устаю, вечером желания ковырять нет никаких  :-\

Оффлайн Sly_tom_cat

  • Автор темы
  • Don't worry, be happy!
  • Заслуженный пользователь
  • Старожил
  • *
  • Сообщений: 12130
  • Xubuntu 22.04
    • Просмотр профиля
    • Github
Re: Клиент синхронизации с Yandex.disk (Python3, OpenSource)
« Ответ #26 : 19 Октября 2016, 00:08:59 »
Punko, спасибо за желание потестировать, но пока клиент еще в очень глубоком "in progress"... т.е. даже не альфа ни разу....

Собственно шебанг мне пока не нужен был т.к. я из Geany запускаю в процессе разработки. Но я кончно добавил, спасибо за замечание.

Версия питона: минимально на чем тестировалось - 3.4 (у меня на работе), но по функционалу библиотек стараюсь брать такое, что от 3.2 и выше.

OAuth.info это файл где лежат ID и пароль приложения, я его внес в .gitignore, что бы не светить на гите. Я еще пока так и не придумал что с ними сделать что бы они не светились в открытом виде в тексте программы... :idiot2:

Файлик я тебе пришлю - его нужно в общую папку к клиенту закинуть.
Индикатор для Yandex-Disk: https://forum.ubuntu.ru/index.php?topic=241992
UEFI-Boot - грузимся без загрузчика: https://help.ubuntu.ru/wiki/uefiboot

Оффлайн Sly_tom_cat

  • Автор темы
  • Don't worry, be happy!
  • Заслуженный пользователь
  • Старожил
  • *
  • Сообщений: 12130
  • Xubuntu 22.04
    • Просмотр профиля
    • Github
Re: Клиент синхронизации с Yandex.disk (Python3, OpenSource)
« Ответ #27 : 19 Октября 2016, 00:19:25 »
По разработке:

Вроде как удалось поженить систему отлова событий в каталоге и процедуру полной сверки и синхронизации.
Решил через создание set, в котором храню создаваемые локально каталоги (когда они есть на облаке и их нет локально) и загружаемые файлы.

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

Сделал сначала через список исключений для iNotify вотчера, т.е. что бы события по загружаемым с облака файлам просто не возникали.... Сделал - не заработало. И только тогда вспомнил - в исключения же только каталоги можно задавать.... так что все решение пришлось вычищать и писать по новой.... надо бы почаще комитить себя заставить.... :(
Индикатор для Yandex-Disk: https://forum.ubuntu.ru/index.php?topic=241992
UEFI-Boot - грузимся без загрузчика: https://help.ubuntu.ru/wiki/uefiboot

Punko

  • Гость
Re: Клиент синхронизации с Yandex.disk (Python3, OpenSource)
« Ответ #28 : 19 Октября 2016, 10:07:16 »
OAuth.info это файл где лежат ID и пароль приложения, я его внес в .gitignore, что бы не светить на гите. Я еще пока так и не придумал что с ними сделать что бы они не светились в открытом виде в тексте программы... :idiot2:

было б неплохо где-то его хранить в сети и запрашивать только в нужный момент.
Можно зашифровать ещё каким-нибудь GPG.
Ты у яндекса не спрашивал?
Я, наверно, напишу им в ТП.
надо бы почаще комитить себя заставить.... :(
плагинов нет для автокомита никаких?


Оффлайн Sly_tom_cat

  • Автор темы
  • Don't worry, be happy!
  • Заслуженный пользователь
  • Старожил
  • *
  • Сообщений: 12130
  • Xubuntu 22.04
    • Просмотр профиля
    • Github
Re: Клиент синхронизации с Yandex.disk (Python3, OpenSource)
« Ответ #29 : 19 Октября 2016, 11:39:01 »
Punko, авто-коммиты - это зло. Они плодят туеву хучу коммитов в которых потом запаришься искать.
Я же разрабатываю довольно интерактивно - напишу кусочек, обвешаю затычками, запущу посмотрю - как оно.... пару заглушек заменю рабочими компонентами - снова запущу, записей исходников на диск - очень много. по каждой записи коммит - это будет полный бардак.

Я просто не слишком то профессиональный разработчик - так любитель. И у меня не выработана нужная привычка: допилил до хоть как-то работающего состояния - закомить. А т.к. тут сейчас идет по сути поиск в разработке то даже и не знаешь как коммитить на каждую идею новый бранч заводить - заблудишься в бранчах.

Про ID и пароль я не вижу путей его скрыть. Не вижу от слова "совсем".
Если брать из сети, то любой, кто посмотрит на код точно так же возьмет его из сети.
GPG и любые другие способы шифрования бессмысленны т.к. ключ для расшифровывания должен быть доступен приложению и опять - есть ключ - бери расшифровывай.
Но если даже ты как-то скроешь (скорее просто сильно усложнишь) получение ID и пароля, в запросе то они пойдут в открытом виде: банальный tcpdump и "ключик у нас в кармане". Ну и нафига тогда городить огород?

Поэтому все больше склоняюсь к тому что бы тупо вставить их в код как константы.
« Последнее редактирование: 19 Октября 2016, 11:40:58 от Sly_tom_cat »
Индикатор для Yandex-Disk: https://forum.ubuntu.ru/index.php?topic=241992
UEFI-Boot - грузимся без загрузчика: https://help.ubuntu.ru/wiki/uefiboot

 

Страница сгенерирована за 0.048 секунд. Запросов: 23.