summaryrefslogtreecommitdiffstats
path: root/pavement.py
diff options
context:
space:
mode:
Diffstat (limited to 'pavement.py')
-rw-r--r--pavement.py437
1 files changed, 252 insertions, 185 deletions
diff --git a/pavement.py b/pavement.py
index ac9a6fa1a..ea6775b1a 100644
--- a/pavement.py
+++ b/pavement.py
@@ -1,84 +1,44 @@
# -*- coding: utf-8 -*-
-
from paver.easy import *
-from paver.setuputils import setup
from paver.doctools import cog
+import fnmatch
+
+# patch to let it support list of patterns
+def new_fnmatch(self, pattern):
+ if type(pattern) == list:
+ for p in pattern:
+ if fnmatch.fnmatch(self.name, p):
+ return True
+ return False
+ else:
+ return fnmatch.fnmatch(self.name, pattern)
+
+
+path.fnmatch = new_fnmatch
+
+import os
import sys
+import shutil
import re
-from urllib import urlretrieve
-from subprocess import call, Popen, PIPE
-from zipfile import ZipFile
+from glob import glob
+from tempfile import mkdtemp
+from subprocess import call, Popen
PROJECT_DIR = path(__file__).dirname()
sys.path.append(PROJECT_DIR)
-options = environment.options
-path('pyload').mkdir()
-
-extradeps = []
-if sys.version_info <= (2, 5):
- extradeps += 'simplejson'
-
-setup(
- name="pyload",
- version="0.4.9",
- description='Fast, lightweight and full featured download manager.',
- long_description=open(PROJECT_DIR / "README").read(),
- keywords = ('pyload', 'download-manager', 'one-click-hoster', 'download'),
- url="http://pyload.org",
- download_url='http://pyload.org/download',
- license='GPL v3',
- author="pyLoad Team",
- author_email="support@pyload.org",
- platforms = ('Any',),
- #package_dir={'pyload': 'src'},
- packages=['pyload'],
- #package_data=find_package_data(),
- #data_files=[],
- include_package_data=True,
- exclude_package_data={'pyload': ['docs*', 'scripts*', 'tests*']}, #exluced from build but not from sdist
- # 'bottle >= 0.10.0' not in list, because its small and contain little modifications
- install_requires=['thrift >= 0.8.0', 'jinja2', 'pycurl', 'Beaker', 'BeautifulSoup>=3.2, <3.3'] + extradeps,
- extras_require={
- 'SSL': ["pyOpenSSL"],
- 'DLC': ['pycrypto'],
- 'lightweight webserver': ['bjoern'],
- 'RSS plugins': ['feedparser'],
- },
- #setup_requires=["setuptools_hg"],
- entry_points={
- 'console_scripts': [
- 'pyLoadCore = pyLoadCore:main',
- 'pyLoadCli = pyLoadCli:main'
- ]},
- zip_safe=False,
- classifiers=[
- "Development Status :: 5 - Production/Stable",
- "Topic :: Internet :: WWW/HTTP",
- "Environment :: Console",
- "Environment :: Web Environment",
- "Intended Audience :: End Users/Desktop",
- "License :: OSI Approved :: GNU General Public License (GPL)",
- "Operating System :: OS Independent",
- "Programming Language :: Python :: 2"
- ]
-)
+from pyload import __version__
+options = environment.options
options(
sphinx=Bunch(
builddir="_build",
sourcedir=""
),
- get_source=Bunch(
- src="https://bitbucket.org/spoob/pyload/get/tip.zip",
- rev=None,
- clean=False
- ),
- thrift=Bunch(
- path="../thrift/trunk/compiler/cpp/thrift",
- gen=""
+ apitypes=Bunch(
+ path="thrift",
),
virtualenv=Bunch(
dir="env",
@@ -86,165 +46,212 @@ options(
virtual="virtualenv2",
),
cog=Bunch(
- pattern="*.py",
+ pattern=["*.py", "*.rst"],
)
)
# xgettext args
-xargs = ["--from-code=utf-8", "--copyright-holder=pyLoad Team", "--package-name=pyLoad",
- "--package-version=%s" % options.version, "--msgid-bugs-address='bugs@pyload.org'"]
+xargs = ["--language=Python", "--add-comments=L10N",
+ "--from-code=utf-8", "--copyright-holder=pyLoad Team", "--package-name=pyload",
+ "--package-version=%s" % __version__, "--msgid-bugs-address='bugs@pyload.org'"]
+
+# Modules replace rules
+module_replace = [
+ ('from module.plugins.Hoster import Hoster', 'from pyload.plugins.Hoster import Hoster'),
+ ('from module.plugins.Hook import threaded, Expose, Hook',
+ 'from pyload.plugins.Addon import threaded, Expose, Addon'),
+ ('from module.plugins.Hook import Hook', 'from pyload.plugins.Addon import Addon'),
+ ('from module.common.json_layer import json_loads, json_dumps', 'from pyload.utils import json_loads, json_dumps'),
+ ('from module.common.json_layer import json_loads', 'from pyload.utils import json_loads'),
+ ('from module.common.json_layer import json_dumps', 'from pyload.utils import json_dumps'),
+ ('from module.utils import parseFileSize', 'from pyload.utils import parseFileSize'),
+ ('from module.utils import save_join, save_path',
+ 'from pyload.utils.fs import save_join, save_filename as save_path'),
+ ('from module.utils import save_join, fs_encode', 'from pyload.utils.fs import save_join, fs_encode'),
+ ('from module.utils import save_join', 'from pyload.utils.fs import save_join'),
+ ('from module.utils import fs_encode', 'from pyload.utils.fs import fs_encode'),
+ ('from module.unescape import unescape', 'from pyload.utils import html_unescape as unescape'),
+ ('from module.lib.BeautifulSoup import BeautifulSoup', 'from BeautifulSoup import BeautifulSoup'),
+ ('from module.lib import feedparser', 'import feedparser'),
+ ('self.account.getAccountInfo(self.user, ', 'self.account.getAccountData('),
+ ('self.account.getAccountInfo(self.user)', 'self.account.getAccountData()'),
+ ('self.account.accounts[self.user]["password"]', 'self.account.password'),
+ ("self.account.accounts[self.user]['password']", 'self.account.password'),
+ ('from module.', 'from pyload.') # This should be always the last one
+]
+
@task
@needs('cog')
def html():
"""Build html documentation"""
- module = path("docs") / "module"
+ module = path("docs") / "pyload"
module.rmtree()
call_task('paver.doctools.html')
@task
@cmdopts([
- ('src=', 's', 'Url to source'),
- ('rev=', 'r', "HG revision"),
- ("clean", 'c', 'Delete old source folder')
+ ('path=', 'p', 'Thrift path'),
])
-def get_source(options):
- """ Downloads pyload source from bitbucket tip or given rev"""
- if options.rev: options.url = "https://bitbucket.org/spoob/pyload/get/%s.zip" % options.rev
+def apitypes(options):
+ """ Generate data types stubs """
- pyload = path("pyload")
+ outdir = PROJECT_DIR / "pyload" / "remote"
- if len(pyload.listdir()) and not options.clean:
- return
- elif pyload.exists():
- pyload.rmtree()
+ if (outdir / "gen-py").exists():
+ (outdir / "gen-py").rmtree()
- urlretrieve(options.src, "pyload_src.zip")
- zip = ZipFile("pyload_src.zip")
- zip.extractall()
- path("pyload_src.zip").remove()
+ cmd = [options.apitypes.path, "-strict", "-o", outdir, "--gen", "py:slots,dynamic", outdir / "pyload.thrift"]
- folder = [x for x in path(".").dirs() if x.name.startswith("spoob-pyload-")][0]
- folder.move(pyload)
+ print "running", cmd
- change_mode(pyload, 0644)
- change_mode(pyload, 0755, folder=True)
+ p = Popen(cmd)
+ p.communicate()
- for file in pyload.files():
- if file.name.endswith(".py"):
- file.chmod(0755)
+ (outdir / "thriftgen").rmtree()
+ (outdir / "gen-py").move(outdir / "thriftgen")
- (pyload / ".hgtags").remove()
- (pyload / ".hgignore").remove()
- #(pyload / "docs").rmtree()
+ #create light ttypes
+ from pyload.remote.create_apitypes import main
- f = open(pyload / "__init__.py", "wb")
- f.close()
+ main()
+ from pyload.remote.create_jstypes import main
- #options.setup.packages = find_packages()
- #options.setup.package_data = find_package_data()
+ main()
@task
-@needs('clean', 'generate_setup', 'minilib', 'get_source', 'setuptools.command.sdist')
-def sdist():
- """ Build source code package with distutils """
+def webapp():
+ """ Builds the pyload web app. Nodejs and npm must be installed """
+
+ os.chdir(PROJECT_DIR / "pyload" / "web")
+
+ # Preserve exit codes
+ ret = call(["npm", "install", "--no-color"])
+ if ret:
+ exit(ret)
+ ret = call(["bower", "install", "--no-color"])
+ if ret:
+ exit(ret)
+ ret = call(["bower", "update", "--no-color"])
+ if ret:
+ exit(ret)
+ ret = call(["grunt", "--no-color"])
+ if ret:
+ exit(ret)
@task
-@cmdopts([
- ('path=', 'p', 'Thrift path'),
- ('gen=', 'g', "Extra --gen option")
-])
-def thrift(options):
- """ Generate Thrift stubs """
-
- print "add import for TApplicationException manually as long it is not fixed"
-
- outdir = path("module") / "remote" / "thriftbackend"
- (outdir / "gen-py").rmtree()
+def generate_locale():
+ """ Generates localisation files """
- cmd = [options.thrift.path, "-strict", "-o", outdir, "--gen", "py:slots,dynamic", outdir / "pyload.thrift"]
+ EXCLUDE = ["pyload/lib", "pyload/cli", "pyload/setup", "pyload/plugins", "Setup.py"]
- if options.gen:
- cmd.insert(len(cmd) - 1, "--gen")
- cmd.insert(len(cmd) - 1, options.gen)
+ makepot("core", path("pyload"), EXCLUDE)
+ makepot("plugins", path("pyload") / "plugins")
+ makepot("setup", "", [], includes="./pyload/setup/Setup.py\n")
+ makepot("cli", path("pyload") / "cli", [])
+ makepot("webUI", path("pyload") / "web" / "app", ["components", "vendor", "gettext"], endings=[".js", ".html"],
+ xxargs="--language=Python --force-po".split(" "))
- print "running", cmd
+ makehtml("webUI", path("pyload") / "web" / "app" / "templates")
- p = Popen(cmd)
- p.communicate()
+ path("includes.txt").remove()
- (outdir / "thriftgen").rmtree()
- (outdir / "gen-py").move(outdir / "thriftgen")
+ print "Locale generated"
- #create light ttypes
- from module.remote.socketbackend.create_ttypes import main
- main()
@task
-def compile_js():
- """ Compile .coffee files to javascript"""
-
- root = path("module") / "web" / "media" / "js"
- for f in root.glob("*.coffee"):
- print "generate", f
- coffee = Popen(["coffee", "-cbs"], stdin=open(f, "rb"), stdout=PIPE)
- yui = Popen(["yuicompressor", "--type", "js"], stdin=coffee.stdout, stdout=PIPE)
- coffee.stdout.close()
- content = yui.communicate()[0]
- with open(root / f.name.replace(".coffee", ".js"), "wb") as js:
- js.write("{% autoescape true %}\n")
- js.write(content)
- js.write("\n{% endautoescape %}")
+@cmdopts([
+ ('key=', 'k', 'api key')
+])
+def upload_translations(options):
+ """ Uploads the locale files to translation server """
+ tmp = path(mkdtemp())
+
+ shutil.copy('locale/crowdin.yaml', tmp)
+ os.mkdir(tmp / 'pyLoad')
+ for f in glob('locale/*.pot'):
+ if os.path.isfile(f):
+ shutil.copy(f, tmp / 'pyLoad')
+
+ config = tmp / 'crowdin.yaml'
+ content = open(config, 'rb').read()
+ content = content.format(key=options.key, tmp=tmp)
+ f = open(config, 'wb')
+ f.write(content)
+ f.close()
+ call(['crowdin-cli', '-c', config, 'upload', 'source'])
-@task
-def generate_locale():
- """ Generates localisation files """
+ shutil.rmtree(tmp)
- EXCLUDE = ["BeautifulSoup.py", "module/gui", "module/cli", "web/locale", "web/ajax", "web/cnl", "web/pyload",
- "setup.py"]
- makepot("core", path("module"), EXCLUDE, "./pyLoadCore.py\n")
+ print "Translations uploaded"
- makepot("gui", path("module") / "gui", [], includes="./pyLoadGui.py\n")
- makepot("cli", path("module") / "cli", [], includes="./pyLoadCli.py\n")
- makepot("setup", "", [], includes="./module/setup.py\n")
- EXCLUDE = ["ServerThread.py", "web/media/default"]
-
- # strings from js files
- strings = set()
+@task
+@cmdopts([
+ ('key=', 'k', 'api key')
+])
+def download_translations(options):
+ """ Downloads the translated files from translation server """
+ tmp = path(mkdtemp())
+
+ shutil.copy('locale/crowdin.yaml', tmp)
+ os.mkdir(tmp / 'pyLoad')
+ for f in glob('locale/*.pot'):
+ if os.path.isfile(f):
+ shutil.copy(f, tmp / 'pyLoad')
+
+ config = tmp / 'crowdin.yaml'
+ content = open(config, 'rb').read()
+ content = content.format(key=options.key, tmp=tmp)
+ f = open(config, 'wb')
+ f.write(content)
+ f.close()
- for fi in path("module/web").walkfiles():
- if not fi.name.endswith(".js") and not fi.endswith(".coffee"): continue
- with open(fi, "rb") as c:
- content = c.read()
+ call(['crowdin-cli', '-c', config, 'download'])
- strings.update(re.findall(r"_\s*\(\s*\"([^\"]+)", content))
- strings.update(re.findall(r"_\s*\(\s*\'([^\']+)", content))
+ for language in (tmp / 'pyLoad').listdir():
+ if not language.isdir():
+ continue
- trans = path("module") / "web" / "translations.js"
+ target = path('locale') / language.basename()
+ print "Copy language %s" % target
+ if target.exists():
+ shutil.rmtree(target)
- with open(trans, "wb") as js:
- for s in strings:
- js.write('_("%s")\n' % s)
+ shutil.copytree(language, target)
- makepot("django", path("module/web"), EXCLUDE, "./%s\n" % trans.relpath(), [".py", ".html"], ["--language=Python"])
+ shutil.rmtree(tmp)
- trans.remove()
- path("includes.txt").remove()
+@task
+def compile_translations():
+ """ Compile PO files to MO """
+ for language in path('locale').listdir():
+ if not language.isdir():
+ continue
- print "Locale generated"
+ for f in glob(language / 'LC_MESSAGES' / '*.po'):
+ print "Compiling %s" % f
+ call(['msgfmt', '-o', f.replace('.po', '.mo'), f])
@task
def tests():
- call(["nosetests2"])
+ """ Run complete test suite """
+ call(["tests/run_pyload.sh"])
+ call(["tests/nosetests.sh"])
+ call(["tests/quit_pyload.sh"])
+
@task
+@cmdopts([
+ ('virtual=', 'v', 'virtualenv path'),
+ ('python=', 'p', 'python path')
+])
def virtualenv(options):
"""Setup virtual environment"""
if path(options.dir).exists():
@@ -263,28 +270,41 @@ def clean_env():
@task
-@needs('generate_setup', 'minilib', 'get_source', 'virtualenv')
-def env_install():
- """Install pyLoad into the virtualenv"""
- venv = options.virtualenv
- call([path(venv.dir) / "bin" / "easy_install", "."])
-
-
-@task
def clean():
"""Cleans build directories"""
path("build").rmtree()
path("dist").rmtree()
+@task
+def replace_module_imports():
+ """Replace imports from stable syntax to master"""
+ for root, dirnames, filenames in os.walk('pyload/plugins'):
+ for filename in fnmatch.filter(filenames, '*.py'):
+ path = os.path.join(root, filename)
+ f = open(path, 'r')
+ content = f.read()
+ f.close()
+ for rule in module_replace:
+ content = content.replace(rule[0], rule[1])
+ if '/addons/' in path:
+ content = content.replace('(Hook):', '(Addon):')
+ elif '/accounts/' in path:
+ content = content.replace('self.accounts[user]["password"]', 'self.password')
+ content = content.replace("self.accounts[user]['password']", 'self.password')
+ f = open(path, 'w')
+ f.write(content)
+ f.close()
+
+
#helper functions
-def walk_trans(path, EXCLUDE, endings=[".py"]):
+def walk_trans(path, excludes, endings=[".py"]):
result = ""
for f in path.walkfiles():
- if [True for x in EXCLUDE if x in f.dirname().relpath()]: continue
- if f.name in EXCLUDE: continue
+ if [True for x in excludes if x in f.dirname().relpath()]: continue
+ if f.name in excludes: continue
for e in endings:
if f.name.endswith(e):
@@ -318,15 +338,62 @@ def makepot(domain, p, excludes=[], includes="", endings=[".py"], xxargs=[]):
with open("locale/%s.pot" % domain, "wb") as f:
f.write(content)
+def makehtml(domain, p):
+ """ Parses entries from html and append them to existing pot file"""
+
+ pot = path("locale") / "%s.pot" % domain
+
+ with open(pot, 'rb') as f:
+ content = f.readlines()
+
+ msgids = {}
+ # parse existing ids and line
+ for i, line in enumerate(content):
+ if line.startswith("msgid "):
+ msgid = line[6:-1].strip('"')
+ msgids[msgid] = i
+
+ # TODO: parses only n=2 plural
+ single = re.compile(r'\{\{ ?(?:gettext|_) "((?:\\.|[^"\\])*)" ?\}\}')
+ plural = re.compile(r'\{\{ ?(?:ngettext) *"((?:\\.|[^"\\])*)" *"((?:\\.|[^"\\])*)"')
+
+ for f in p.walkfiles():
+ if not f.endswith("html"): continue
+ with open(f, "rb") as html:
+ for i, line in enumerate(html.readlines()):
+ key = None
+ nmessage = plural.search(line)
+ message = single.search(line)
+ if nmessage:
+ key = nmessage.group(1)
+ keyp = nmessage.group(2)
+
+ if key not in msgids:
+ content.append("\n")
+ content.append('msgid "%s"\n' % key)
+ content.append('msgid_plural "%s"\n' % keyp)
+ content.append('msgstr[0] ""\n')
+ content.append('msgstr[1] ""\n')
+ msgids[key] = len(content) - 4
+
+
+ elif message:
+ key = message.group(1)
+
+ if key not in msgids:
+ content.append("\n")
+ content.append('msgid "%s"\n' % key)
+ content.append('msgstr ""\n')
+ msgids[key] = len(content) - 2
+
+ if key:
+ content.insert(msgids[key], "#: %s:%d\n" % (f, i))
+ msgids[key] += 1
+
+
+ with open(pot, 'wb') as f:
+ f.writelines(content)
-def change_owner(dir, uid, gid):
- for p in dir.walk():
- p.chown(uid, gid)
+ print "Parsed html files"
-def change_mode(dir, mode, folder=False):
- for p in dir.walk():
- if folder and p.isdir():
- p.chmod(mode)
- elif p.isfile() and not folder:
- p.chmod(mode)