Поскольку я только что решил выяснить, как это сделать, я полагаю, что с тем же успехом могу включить его в качестве ответа, даже если он к вам не относится:
Прежде чем продолжить, поклянись на могиле твоей матери, что в следующий раз ты будешь делать резервные копии своего кода или, что лучше, использовать систему контроля версий. Я имею в виду: повторите за мной " в следующий раз, когда я буду использовать контроль источника ". Хорошо, с этим покончено, давайте посмотрим, возможно ли восстановить ваш код для вас ...
Если ваше приложение написано на Java, боюсь, вам не повезло - исходный код даже не загружается в App Engine для приложений Java.
Если ваше приложение было написано на Python и для него определены обработчики remote_api и deferred , вы можете восстановить исходный код с помощью взаимодействия этих двух API. Основной трюк выглядит так:
- Запустить remote_api_shell
- Создайте новую отложенную задачу, которая читает все ваши файлы и записывает их в хранилище данных
- Дождаться выполнения этой задачи
- Извлеките ваши данные из хранилища данных, используя remote_api
Глядя на них по порядку:
Запуск remote_api_shell
Просто введите в командной строке следующее:
remote_api_shell.py your_app_id
Если в вашем пути нет оболочки, добавьте к команде префикс с путем к каталогу SDK App Engine.
Запись вашего источника в хранилище данных
Здесь мы собираемся воспользоваться тем фактом, что у вас установлен обработчик deferred, что вы можете использовать remote_api для постановки в очередь задач для deferred и что вы можете отложить вызов встроенной функции Python 'eval' .
Это немного усложняется тем фактом, что 'eval' выполняет только один оператор, а не произвольный блок кода, поэтому нам нужно сформулировать весь код как один оператор. Вот оно:
expr = """
[type(
'CodeFile',
(__import__('google.appengine.ext.db').appengine.ext.db.Expando,),
{})(
name=dp+'/'+fn,
data=__import__('google.appengine.ext.db').appengine.ext.db.Text(
open(dp + '/' + fn).read()
)
).put()
for dp, dns, fns in __import__('os').walk('.')
for fn in fns]
"""
from google.appengine.ext.deferred import defer
defer(eval, expr)
Довольно взломать. Давайте посмотрим на это немного за раз:
Сначала мы используем встроенную функцию type для динамического создания нового подкласса db.Expando . Три аргумента type()
- это имя нового класса, список родительских классов и переменная класса. Все первые 4 строки выражения эквивалентны этому:
from google.appengine.ext import db
class CodeFile(db.Expando): pass
Использование ' import ' здесь является еще одним обходным решением для того факта, что мы не можем использовать операторы: выражение __import__('google.appengine.ext.db')
импортирует указанный модуль и возвращает модуль верхнего уровня (Google) .
Поскольку type()
возвращает новый класс, теперь у нас есть подкласс Expando, который мы можем использовать для хранения данных в хранилище данных. Затем мы вызываем его конструктор, передавая ему два аргумента: «имя» и «данные». Имя, которое мы создаем из конкатенации каталога и файла, с которым мы сейчас имеем дело, в то время как данные являются результатом открытия этого имени файла и чтения его содержимого, обернутого в объект db.Text, поэтому оно может быть произвольно длинным. Наконец, мы вызываем .put () для возвращенного экземпляра, чтобы сохранить его в хранилище данных.
Чтобы прочитать и сохранить весь исходный код, а не только один файл, все это выражение помещается в понимание списка, которое сначала перебирает результат os.walk , который удобно возвращает все каталоги и файлы в базовом каталоге, а затем поверх каждого файла в каждом из этих каталогов. Возвращаемое значение этого выражения - список ключей, которые были записаны в хранилище данных - просто отбрасывается отложенным модулем. Это не имеет значения, так как нас интересуют только побочные эффекты.
Наконец, мы вызываем функцию defer, откладывающую вызов eval, с выражением, которое мы только что описали в качестве аргумента.
Считывание данных
После выполнения описанного выше и ожидания его завершения мы можем извлечь данные из хранилища данных, снова используя remote_api. Во-первых, нам нужна локальная версия модели кода файла:
import os
from google.appengine.ext import db
class CodeFile(db.Model):
name = db.StringProperty(required=True)
data = db.TextProperty(required=True)
Теперь мы можем извлечь все его сущности, сохранив их на диске:
for cf in CodeFile.all():
os.makedirs(os.dirname(cf.name))
fh = open(cf.name, "w")
fh.write(cf.data)
fh.close()
Вот и все! Ваша локальная файловая система должна теперь содержать ваш исходный код.
Одно предупреждение: скачанный код будет содержать только ваш код и файлы данных. Статические файлы не включены, хотя вы должны иметь возможность просто загрузить их по HTTP, если вы помните, что они все. Файлы конфигурации, такие как app.yaml, также не включены и не могут быть восстановлены - вам нужно их переписать. Тем не менее, это намного лучше, чем переписывать все приложение, верно?