<commit_before>
import lixian_download_tools
from lixian_commands.util import *
from lixian_cli_parser import *
from lixian_config import *
from lixian_encoding import default_encoding
from lixian_colors import colors
import lixian_help
import lixian_query
import lixian_hash
import lixian_hash_bt
import lixian_hash_ed2k
import os
import os.path
import re

def escape_filename(name):
	amp = re.compile(r'&(amp;)+', flags=re.I)
	name = re.sub(amp, '&', name)
	name = re.sub(r'[\\/:*?"<>|]', '-', name)
	return name

def verify_basic_hash(path, task):
	if os.path.getsize(path) != task['size']:
		print 'hash error: incorrect file size (%s != %s)' % (os.path.getsize(path), task['size'])
		return False
	return lixian_hash.verify_dcid(path, task['dcid'])

def verify_hash(path, task):
	if verify_basic_hash(path, task):
		if task['type'] == 'ed2k':
			return lixian_hash_ed2k.verify_ed2k_link(path, task['original_url'])
		else:
			return True

def verify_mini_hash(path, task):
	return os.path.exists(path) and os.path.getsize(path) == task['size'] and lixian_hash.verify_dcid(path, task['dcid'])

def verify_mini_bt_hash(dirname, files):
	for f in files:
		name = f['name'].encode(default_encoding)
		path = os.path.join(dirname, *name.split('\\'))
		if not verify_mini_hash(path, f):
			return False
	return True

def download_file(client, path, task, options):
	download_tool = lixian_download_tools.get_tool(options['tool'])

	resuming = options.get('resuming')
	overwrite = options.get('overwrite')
	mini_hash = options.get('mini_hash')
	no_hash = options.get('no_hash')

	url = str(task['xunlei_url'])

	def download1(download, path):
		if not os.path.exists(path):
			download()
		elif not resuming:
			if overwrite:
				download()
			else:
				raise Exception('%s already exists. Please try --continue or --overwrite' % path)
		else:
			if download.finished():
				pass
			else:
				download()

	def download1_checked(client, url, path, size):
		download = download_tool(client=client, url=url, path=path, size=size, resuming=resuming)
		checked = 0
		while checked < 10:
			download1(download, path)
			if download.finished():
				break
			else:
				checked += 1
		assert os.path.getsize(path) == size, 'incorrect downloaded file size (%s != %s)' % (os.path.getsize(path), size)

	def download2(client, url, path, task):
		size = task['size']
		if mini_hash and resuming and verify_mini_hash(path, task):
			return
		download1_checked(client, url, path, size)
		verify = verify_basic_hash if no_hash else verify_hash
		if not verify(path, task):
			with colors(options.get('colors')).yellow():
				print 'hash error, redownloading...'
			os.rename(path, path + '.error')
			download1_checked(client, url, path, size)
			if not verify(path, task):
				raise Exception('hash check failed')

	download2(client, url, path, task)

def download_single_task(client, task, options):
	output = options.get('output')
	output = output and os.path.expanduser(output)
	output_dir = options.get('output_dir')
	output_dir = output_dir and os.path.expanduser(output_dir)
	delete = options.get('delete')
	resuming = options.get('resuming')
	overwrite = options.get('overwrite')
	mini_hash = options.get('mini_hash')
	no_hash = options.get('no_hash')
	no_bt_dir = options.get('no_bt_dir')
	save_torrent_file = options.get('save_torrent_file')

	assert client.get_gdriveid()
	if task['status_text'] != 'completed':
		if 'files' not in task:
			with colors(options.get('colors')).yellow():
				print 'skip task %s as the status is %s' % (task['name'].encode(default_encoding), task['status_text'])
			return

	if output:
		output_path = output
		output_dir = os.path.dirname(output)
		output_name = os.path.basename(output)
	else:
		output_name = escape_filename(task['name']).encode(default_encoding)
		output_dir = output_dir or '.'
		output_path = os.path.join(output_dir, output_name)

	if task['type'] == 'bt':
		files, skipped, single_file = lixian_query.expand_bt_sub_tasks(task)
		if single_file:
			dirname = output_dir
		else:
			if no_bt_dir:
				output_path = os.path.dirname(output_path)
			dirname = output_path
		assert dirname # dirname must be non-empty, otherwise dirname + os.path.sep + ... might be dangerous
		if dirname and not os.path.exists(dirname):
			os.makedirs(dirname)
		for t in skipped:
			with colors(options.get('colors')).yellow():
				print 'skip task %s/%s (%s) as the status is %s' % (str(t['id']), t['index'], t['name'].encode(default_encoding), t['status_text'])
		if mini_hash and resuming and verify_mini_bt_hash(dirname, files):
			print task['name'].encode(default_encoding), 'is already done'
			if delete and 'files' not in task:
				client.delete_task(task)
			return
		if not single_file:
			with colors(options.get('colors')).green():
				print output_name + '/'
		for f in files:
			name = f['name']
			if f['status_text'] != 'completed':
				print 'Skipped %s file %s ...' % (f['status_text'], name.encode(default_encoding))
				continue
			if not single_file:
				print name.encode(default_encoding), '...'
			else:
				with colors(options.get('colors')).green():
					print name.encode(default_encoding), '...'
			# XXX: if file name is escaped, hashing bt won't get correct file
			splitted_path = map(escape_filename, name.split('\\'))
			name = os.path.join(*splitted_path).encode(default_encoding)
			path = dirname + os.path.sep + name # fix issue #82
			if splitted_path[:-1]:
				subdir = os.path.join(*splitted_path[:-1]).encode(default_encoding)
				subdir = dirname + os.path.sep + subdir # fix issue #82
				if not os.path.exists(subdir):
					os.makedirs(subdir)
			download_file(client, path, f, options)
		if save_torrent_file:
			info_hash = str(task['bt_hash'])
			if single_file:
				torrent = os.path.join(dirname, escape_filename(task['name']).encode(default_encoding) + '.torrent')
			else:
				torrent = os.path.join(dirname, info_hash + '.torrent')
			if os.path.exists(torrent):
				pass
			else:
				content = client.get_torrent_file_by_info_hash(info_hash)
				with open(torrent, 'wb') as ouput_stream:
					ouput_stream.write(content)
		if not no_hash:
			torrent_file = client.get_torrent_file(task)
			print 'Hashing bt ...'
			from lixian_progress import SimpleProgressBar
			bar = SimpleProgressBar()
			file_set = [f['name'].encode('utf-8').split('\\') for f in files] if 'files' in task else None
			verified = lixian_hash_bt.verify_bt(output_path, lixian_hash_bt.bdecode(torrent_file)['info'], file_set=file_set, progress_callback=bar.update)
			bar.done()
			if not verified:
				# note that we don't delete bt download folder if hash failed
				raise Exception('bt hash check failed')
	else:
		if output_dir and not os.path.exists(output_dir):
			os.makedirs(output_dir)

		with colors(options.get('colors')).green():
			print output_name, '...'
		download_file(client, output_path, task, options)

	if delete and 'files' not in task:
		client.delete_task(task)

def download_multiple_tasks(client, tasks, options):
	for task in tasks:
		download_single_task(client, task, options)
	skipped = filter(lambda t: t['status_text'] != 'completed', tasks)
	if skipped:
		with colors(options.get('colors')).yellow():
			print "Below tasks were skipped as they were not ready:"
		for task in skipped:
			print task['id'], task['status_text'], task['name'].encode(default_encoding)

@command_line_parser(help=lixian_help.download)
@with_parser(parse_login)
@with_parser(parse_colors)
@with_parser(parse_logging)
@command_line_value('tool', default=get_config('tool', 'wget'))
@command_line_value('input', alias='i')
@command_line_value('output', alias='o')
@command_line_value('output-dir', default=get_config('output-dir'))
@command_line_option('torrent', alias='bt')
@command_line_option('all')
@command_line_value('category')
@command_line_option('delete', default=get_config('delete'))
@command_line_option('continue', alias='c', default=get_config('continue'))
@command_line_option('overwrite')
@command_line_option('mini-hash', default=get_config('mini-hash'))
@command_line_option('hash', default=get_config('hash', True))
@command_line_option('bt-dir', default=True)
@command_line_option('save-torrent-file')
@command_line_option('watch')
@command_line_option('watch-present')
@command_line_value('watch-interval', default=get_config('watch-interval', '3m'))
def download_task(args):
	assert len(args) or args.input or args.all or args.category, 'Not enough arguments'
	lixian_download_tools.get_tool(args.tool) # check tool
	download_args = {'tool': args.tool,
	                 'output': args.output,
	                 'output_dir': args.output_dir,
	                 'delete': args.delete,
	                 'resuming': args._args['continue'],
	                 'overwrite': args.overwrite,
	                 'mini_hash': args.mini_hash,
	                 'no_hash': not args.hash,
	                 'no_bt_dir': not args.bt_dir,
	                 'save_torrent_file': args.save_torrent_file,
	                 'colors': args.colors}
	client = create_client(args)
	query = lixian_query.build_query(client, args)
	query.query_once()

	def sleep(n):
		assert isinstance(n, (int, basestring)), repr(n)
		import time
		if isinstance(n, basestring):
			n, u = re.match(r'^(\d+)([smh])?$', n.lower()).groups()
			n = int(n) * {None: 1, 's': 1, 'm': 60, 'h': 3600}[u]
		time.sleep(n)

	if args.watch_present:
		assert not args.output, 'not supported with watch option yet'
		tasks = query.pull_completed()
		while True:
			if tasks:
				download_multiple_tasks(client, tasks, download_args)
			if not query.download_jobs:
				break
			if not tasks:
				sleep(args.watch_interval)
			query.refresh_status()
			tasks = query.pull_completed()

	elif args.watch:
		assert not args.output, 'not supported with watch option yet'
		tasks = query.pull_completed()
		while True:
			if tasks:
				download_multiple_tasks(client, tasks, download_args)
			if (not query.download_jobs) and (not query.queries):
				break
			if not tasks:
				sleep(args.watch_interval)
			query.refresh_status()
			query.query_search()
			tasks = query.pull_completed()

	else:
		tasks = query.peek_download_jobs()
		if args.output:
			assert len(tasks) == 1
			download_single_task(client, tasks[0], download_args)
		else:
			download_multiple_tasks(client, tasks, download_args)
<commit_msg>encode file path safely<commit_after>
import lixian_download_tools
from lixian_commands.util import *
from lixian_cli_parser import *
from lixian_config import *
from lixian_encoding import default_encoding
from lixian_colors import colors
import lixian_help
import lixian_query
import lixian_hash
import lixian_hash_bt
import lixian_hash_ed2k
import os
import os.path
import re

def escape_filename(name):
	amp = re.compile(r'&(amp;)+', flags=re.I)
	name = re.sub(amp, '&', name)
	name = re.sub(r'[\\/:*?"<>|]', '-', name)
	return name

def safe_encode_native_path(path):
	return path.encode(default_encoding).decode(default_encoding).replace('?', '-').encode(default_encoding)

def verify_basic_hash(path, task):
	if os.path.getsize(path) != task['size']:
		print 'hash error: incorrect file size (%s != %s)' % (os.path.getsize(path), task['size'])
		return False
	return lixian_hash.verify_dcid(path, task['dcid'])

def verify_hash(path, task):
	if verify_basic_hash(path, task):
		if task['type'] == 'ed2k':
			return lixian_hash_ed2k.verify_ed2k_link(path, task['original_url'])
		else:
			return True

def verify_mini_hash(path, task):
	return os.path.exists(path) and os.path.getsize(path) == task['size'] and lixian_hash.verify_dcid(path, task['dcid'])

def verify_mini_bt_hash(dirname, files):
	for f in files:
		name = f['name'].encode(default_encoding)
		path = os.path.join(dirname, *name.split('\\'))
		if not verify_mini_hash(path, f):
			return False
	return True

def download_file(client, path, task, options):
	download_tool = lixian_download_tools.get_tool(options['tool'])

	resuming = options.get('resuming')
	overwrite = options.get('overwrite')
	mini_hash = options.get('mini_hash')
	no_hash = options.get('no_hash')

	url = str(task['xunlei_url'])

	def download1(download, path):
		if not os.path.exists(path):
			download()
		elif not resuming:
			if overwrite:
				download()
			else:
				raise Exception('%s already exists. Please try --continue or --overwrite' % path)
		else:
			if download.finished():
				pass
			else:
				download()

	def download1_checked(client, url, path, size):
		download = download_tool(client=client, url=url, path=path, size=size, resuming=resuming)
		checked = 0
		while checked < 10:
			download1(download, path)
			if download.finished():
				break
			else:
				checked += 1
		assert os.path.getsize(path) == size, 'incorrect downloaded file size (%s != %s)' % (os.path.getsize(path), size)

	def download2(client, url, path, task):
		size = task['size']
		if mini_hash and resuming and verify_mini_hash(path, task):
			return
		download1_checked(client, url, path, size)
		verify = verify_basic_hash if no_hash else verify_hash
		if not verify(path, task):
			with colors(options.get('colors')).yellow():
				print 'hash error, redownloading...'
			os.rename(path, path + '.error')
			download1_checked(client, url, path, size)
			if not verify(path, task):
				raise Exception('hash check failed')

	download2(client, url, path, task)

def download_single_task(client, task, options):
	output = options.get('output')
	output = output and os.path.expanduser(output)
	output_dir = options.get('output_dir')
	output_dir = output_dir and os.path.expanduser(output_dir)
	delete = options.get('delete')
	resuming = options.get('resuming')
	overwrite = options.get('overwrite')
	mini_hash = options.get('mini_hash')
	no_hash = options.get('no_hash')
	no_bt_dir = options.get('no_bt_dir')
	save_torrent_file = options.get('save_torrent_file')

	assert client.get_gdriveid()
	if task['status_text'] != 'completed':
		if 'files' not in task:
			with colors(options.get('colors')).yellow():
				print 'skip task %s as the status is %s' % (task['name'].encode(default_encoding), task['status_text'])
			return

	if output:
		output_path = output
		output_dir = os.path.dirname(output)
		output_name = os.path.basename(output)
	else:
		output_name = safe_encode_native_path(escape_filename(task['name']))
		output_dir = output_dir or '.'
		output_path = os.path.join(output_dir, output_name)

	if task['type'] == 'bt':
		files, skipped, single_file = lixian_query.expand_bt_sub_tasks(task)
		if single_file:
			dirname = output_dir
		else:
			if no_bt_dir:
				output_path = os.path.dirname(output_path)
			dirname = output_path
		assert dirname # dirname must be non-empty, otherwise dirname + os.path.sep + ... might be dangerous
		if dirname and not os.path.exists(dirname):
			os.makedirs(dirname)
		for t in skipped:
			with colors(options.get('colors')).yellow():
				print 'skip task %s/%s (%s) as the status is %s' % (str(t['id']), t['index'], t['name'].encode(default_encoding), t['status_text'])
		if mini_hash and resuming and verify_mini_bt_hash(dirname, files):
			print task['name'].encode(default_encoding), 'is already done'
			if delete and 'files' not in task:
				client.delete_task(task)
			return
		if not single_file:
			with colors(options.get('colors')).green():
				print output_name + '/'
		for f in files:
			name = f['name']
			if f['status_text'] != 'completed':
				print 'Skipped %s file %s ...' % (f['status_text'], name.encode(default_encoding))
				continue
			if not single_file:
				print name.encode(default_encoding), '...'
			else:
				with colors(options.get('colors')).green():
					print name.encode(default_encoding), '...'
			# XXX: if file name is escaped, hashing bt won't get correct file
			splitted_path = map(escape_filename, name.split('\\'))
			name = safe_encode_native_path(os.path.join(*splitted_path))
			path = dirname + os.path.sep + name # fix issue #82
			if splitted_path[:-1]:
				subdir = os.path.join(*splitted_path[:-1]).encode(default_encoding)
				subdir = dirname + os.path.sep + subdir # fix issue #82
				if not os.path.exists(subdir):
					os.makedirs(subdir)
			download_file(client, path, f, options)
		if save_torrent_file:
			info_hash = str(task['bt_hash'])
			if single_file:
				torrent = os.path.join(dirname, escape_filename(task['name']).encode(default_encoding) + '.torrent')
			else:
				torrent = os.path.join(dirname, info_hash + '.torrent')
			if os.path.exists(torrent):
				pass
			else:
				content = client.get_torrent_file_by_info_hash(info_hash)
				with open(torrent, 'wb') as ouput_stream:
					ouput_stream.write(content)
		if not no_hash:
			torrent_file = client.get_torrent_file(task)
			print 'Hashing bt ...'
			from lixian_progress import SimpleProgressBar
			bar = SimpleProgressBar()
			file_set = [f['name'].encode('utf-8').split('\\') for f in files] if 'files' in task else None
			verified = lixian_hash_bt.verify_bt(output_path, lixian_hash_bt.bdecode(torrent_file)['info'], file_set=file_set, progress_callback=bar.update)
			bar.done()
			if not verified:
				# note that we don't delete bt download folder if hash failed
				raise Exception('bt hash check failed')
	else:
		if output_dir and not os.path.exists(output_dir):
			os.makedirs(output_dir)

		with colors(options.get('colors')).green():
			print output_name, '...'
		download_file(client, output_path, task, options)

	if delete and 'files' not in task:
		client.delete_task(task)

def download_multiple_tasks(client, tasks, options):
	for task in tasks:
		download_single_task(client, task, options)
	skipped = filter(lambda t: t['status_text'] != 'completed', tasks)
	if skipped:
		with colors(options.get('colors')).yellow():
			print "Below tasks were skipped as they were not ready:"
		for task in skipped:
			print task['id'], task['status_text'], task['name'].encode(default_encoding)

@command_line_parser(help=lixian_help.download)
@with_parser(parse_login)
@with_parser(parse_colors)
@with_parser(parse_logging)
@command_line_value('tool', default=get_config('tool', 'wget'))
@command_line_value('input', alias='i')
@command_line_value('output', alias='o')
@command_line_value('output-dir', default=get_config('output-dir'))
@command_line_option('torrent', alias='bt')
@command_line_option('all')
@command_line_value('category')
@command_line_option('delete', default=get_config('delete'))
@command_line_option('continue', alias='c', default=get_config('continue'))
@command_line_option('overwrite')
@command_line_option('mini-hash', default=get_config('mini-hash'))
@command_line_option('hash', default=get_config('hash', True))
@command_line_option('bt-dir', default=True)
@command_line_option('save-torrent-file')
@command_line_option('watch')
@command_line_option('watch-present')
@command_line_value('watch-interval', default=get_config('watch-interval', '3m'))
def download_task(args):
	assert len(args) or args.input or args.all or args.category, 'Not enough arguments'
	lixian_download_tools.get_tool(args.tool) # check tool
	download_args = {'tool': args.tool,
	                 'output': args.output,
	                 'output_dir': args.output_dir,
	                 'delete': args.delete,
	                 'resuming': args._args['continue'],
	                 'overwrite': args.overwrite,
	                 'mini_hash': args.mini_hash,
	                 'no_hash': not args.hash,
	                 'no_bt_dir': not args.bt_dir,
	                 'save_torrent_file': args.save_torrent_file,
	                 'colors': args.colors}
	client = create_client(args)
	query = lixian_query.build_query(client, args)
	query.query_once()

	def sleep(n):
		assert isinstance(n, (int, basestring)), repr(n)
		import time
		if isinstance(n, basestring):
			n, u = re.match(r'^(\d+)([smh])?$', n.lower()).groups()
			n = int(n) * {None: 1, 's': 1, 'm': 60, 'h': 3600}[u]
		time.sleep(n)

	if args.watch_present:
		assert not args.output, 'not supported with watch option yet'
		tasks = query.pull_completed()
		while True:
			if tasks:
				download_multiple_tasks(client, tasks, download_args)
			if not query.download_jobs:
				break
			if not tasks:
				sleep(args.watch_interval)
			query.refresh_status()
			tasks = query.pull_completed()

	elif args.watch:
		assert not args.output, 'not supported with watch option yet'
		tasks = query.pull_completed()
		while True:
			if tasks:
				download_multiple_tasks(client, tasks, download_args)
			if (not query.download_jobs) and (not query.queries):
				break
			if not tasks:
				sleep(args.watch_interval)
			query.refresh_status()
			query.query_search()
			tasks = query.pull_completed()

	else:
		tasks = query.peek_download_jobs()
		if args.output:
			assert len(tasks) == 1
			download_single_task(client, tasks[0], download_args)
		else:
			download_multiple_tasks(client, tasks, download_args)
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- encoding: utf-8 -*-
from __future__ import absolute_import

import io
import re
from glob import glob
from os import path, environ

from setuptools import find_packages

# Include extensions only when not on readthedocs.org
if environ.get('READTHEDOCS', None) == 'True':
    from setuptools import setup
    from distutils.core import Extension
    extensions = []
else:
    from numpy.distutils.core import setup, Extension

    extensions = [
        Extension(name='apexpy.fortranapex',
                  sources=['src/fortranapex/magfld.f', 'src/fortranapex/apex.f',
                           'src/fortranapex/makeapexsh.f90',
                           'src/fortranapex/apexsh.f90',
                           'src/fortranapex/checkapexsh.f90'])]

def read(*names, **kwargs):
    return io.open(
        path.join(path.dirname(__file__), *names),
        encoding=kwargs.get('encoding', 'utf8')
    ).read()

if __name__ == "__main__":
    setup(
        name='apexpy',
        version='1.0.3',
        license='MIT',
        description='A Python wrapper for Apex coordinates',
        long_description='%s\n%s' % (read('README.rst'),
                                     re.sub(':[a-z]+:`~?(.*?)`', r'``\1``',
                                            read('CHANGELOG.rst'))),
        author='Christer van der Meeren; Angeline G. Burrell',
        author_email='agb073000@utdallas.edu',
        url='https://github.com/aburrell/apexpy',
        packages=find_packages('src'),
        package_dir={'': 'src'},
        py_modules=[path.splitext(path.basename(pp))[0]
                    for pp in glob('src/*.py')],
        package_data={'apexpy': ['apexsh.dat']},
        zip_safe=False,
        classifiers=[
            # complete classifier list:
            # http://pypi.python.org/pypi?%3Aaction=list_classifiers
            'Development Status :: 5 - Production/Stable',
            'Intended Audience :: Science/Research',
            'License :: OSI Approved :: MIT License',
            'Operating System :: Unix',
            'Operating System :: POSIX',
            'Operating System :: Microsoft :: Windows',
            'Operating System :: MacOS :: MacOS X',
            'Programming Language :: Python',
            'Programming Language :: Python :: 2.7',
            'Programming Language :: Python :: 3',
            'Programming Language :: Python :: 3.4',
            'Programming Language :: Python :: 3.5',
            'Programming Language :: Python :: 3.6',
            'Programming Language :: Python :: 3.7',
            'Programming Language :: Python :: Implementation :: CPython',
            'Topic :: Scientific/Engineering :: Physics',
            'Topic :: Utilities',
        ],
        keywords=[
            'apex',
            'modified apex',
            'quasi-dipole',
            'quasi dipole',
            'coordinates',
            'magnetic coordinates',
            'mlt',
            'magnetic local time',
            'conversion',
            'converting',
        ],
        install_requires=[
            'numpy',
        ],
        ext_modules=extensions,
        entry_points={
            'console_scripts': [
                'apexpy = apexpy.__main__:main',
            ]
        },
    )
<commit_msg>removed 3.7<commit_after>#!/usr/bin/env python
# -*- encoding: utf-8 -*-
from __future__ import absolute_import

import io
import re
from glob import glob
from os import path, environ

from setuptools import find_packages

# Include extensions only when not on readthedocs.org
if environ.get('READTHEDOCS', None) == 'True':
    from setuptools import setup
    from distutils.core import Extension
    extensions = []
else:
    from numpy.distutils.core import setup, Extension

    extensions = [
        Extension(name='apexpy.fortranapex',
                  sources=['src/fortranapex/magfld.f', 'src/fortranapex/apex.f',
                           'src/fortranapex/makeapexsh.f90',
                           'src/fortranapex/apexsh.f90',
                           'src/fortranapex/checkapexsh.f90'])]

def read(*names, **kwargs):
    return io.open(
        path.join(path.dirname(__file__), *names),
        encoding=kwargs.get('encoding', 'utf8')
    ).read()

if __name__ == "__main__":
    setup(
        name='apexpy',
        version='1.0.3',
        license='MIT',
        description='A Python wrapper for Apex coordinates',
        long_description='%s\n%s' % (read('README.rst'),
                                     re.sub(':[a-z]+:`~?(.*?)`', r'``\1``',
                                            read('CHANGELOG.rst'))),
        author='Christer van der Meeren; Angeline G. Burrell',
        author_email='agb073000@utdallas.edu',
        url='https://github.com/aburrell/apexpy',
        packages=find_packages('src'),
        package_dir={'': 'src'},
        py_modules=[path.splitext(path.basename(pp))[0]
                    for pp in glob('src/*.py')],
        package_data={'apexpy': ['apexsh.dat']},
        zip_safe=False,
        classifiers=[
            # complete classifier list:
            # http://pypi.python.org/pypi?%3Aaction=list_classifiers
            'Development Status :: 5 - Production/Stable',
            'Intended Audience :: Science/Research',
            'License :: OSI Approved :: MIT License',
            'Operating System :: Unix',
            'Operating System :: POSIX',
            'Operating System :: Microsoft :: Windows',
            'Operating System :: MacOS :: MacOS X',
            'Programming Language :: Python',
            'Programming Language :: Python :: 2.7',
            'Programming Language :: Python :: 3',
            'Programming Language :: Python :: 3.4',
            'Programming Language :: Python :: 3.5',
            'Programming Language :: Python :: 3.6',
            'Programming Language :: Python :: Implementation :: CPython',
            'Topic :: Scientific/Engineering :: Physics',
            'Topic :: Utilities',
        ],
        keywords=[
            'apex',
            'modified apex',
            'quasi-dipole',
            'quasi dipole',
            'coordinates',
            'magnetic coordinates',
            'mlt',
            'magnetic local time',
            'conversion',
            'converting',
        ],
        install_requires=[
            'numpy',
        ],
        ext_modules=extensions,
        entry_points={
            'console_scripts': [
                'apexpy = apexpy.__main__:main',
            ]
        },
    )
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

from __future__ import unicode_literals

from datetime import datetime
import jwt
import logging

from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.core.validators import URLValidator
from django.http import HttpResponse
from django.http import HttpResponseBadRequest
from django.http import HttpResponseRedirect
from django.shortcuts import redirect
from django.template.context import RequestContext
from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator
from django.utils.functional import cached_property
from django.utils.translation import ugettext as _
from django.views.generic import View
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView

# urllib is the hardest module to use from Python 2/3 compatible code. Six
# provides a version for wrapping over differences between Python 2 and 3.
from six.moves.urllib import parse

from lizard_auth_server import forms
from lizard_auth_server.conf import settings
from lizard_auth_server.models import Invitation
from lizard_auth_server.models import Portal

JWT_ALGORITHM = settings.LIZARD_AUTH_SERVER_JWT_ALGORITHM
JWT_EXPIRATION_DELTA = settings.LIZARD_AUTH_SERVER_JWT_EXPIRATION_DELTA

logger = logging.getLogger(__name__)

class StaffOnlyMixin(object):
    """
    Ensures access by staff members (user.is_staff is True) only to all
    HTTP methods.

    Ensure this is first in the inheritance list!
    """
    @method_decorator(staff_member_required)
    def dispatch(self, request, *args, **kwargs):
        return super(StaffOnlyMixin, self).dispatch(request, *args, **kwargs)

class ErrorMessageResponse(TemplateResponse):
    """
    Display a slightly more user-friendly error message.
    """
    def __init__(self, request, error_message=None, status=500):
        if not error_message:
            error_message = _('An unknown error occurred.')
        context = RequestContext(
            request,
            {
                'error_message': error_message
            }
        )
        super(ErrorMessageResponse, self).__init__(
            request,
            'lizard_auth_server/error_message.html',
            context,
            status=status
        )

##################################################
# Invitation / registration / activation / profile
##################################################

class ProfileView(TemplateView):
    """
    Straightforward view which displays a user's profile.
    """
    template_name = 'lizard_auth_server/profile.html'

    @cached_property
    def profile(self):
        return self.request.user.user_profile

    @property
    def portals(self):
        if self.request.user.is_staff:
            return Portal.objects.all()
        else:
            return self.profile.portals.all()

    @method_decorator(login_required)
    def dispatch(self, request, *args, **kwargs):
        return super(ProfileView, self).dispatch(request, *args, **kwargs)

class AccessToPortalView(TemplateView):
    template_name = 'lizard_auth_server/access-to-portal.html'

    @method_decorator(login_required)
    def dispatch(self, request, *args, **kwargs):
        return super(AccessToPortalView, self).dispatch(
            request, *args, **kwargs)

    @cached_property
    def portal(self):
        portal_pk = self.kwargs['portal_pk']
        return Portal.objects.get(id=portal_pk)

    @cached_property
    def title(self):
        return _('Access to portal {} for {}').format(self.portal.name,
                                                      self.profile)

    @cached_property
    def profile(self):
        if self.request.user.is_staff:
            user_id = self.kwargs.get('user_pk')
            if user_id:
                user = User.objects.get(id=user_id)
                # User.get_profile is deprecated since Django 1.7
                # return user.get_profile()
                return user.user_profile
        # User.get_profile is deprecated since Django 1.7
        # return self.request.user.get_profile()
        return self.request.user.user_profile

    @cached_property
    def organisation_roles_explanation(self):
        if not self.request.user.is_staff:
            return
        return self.profile.all_organisation_roles(
            self.portal,
            return_explanation=True)

    @cached_property
    def user_profiles_for_portal(self):
        if not self.request.user.is_staff:
            return
        return self.portal.user_profiles.select_related('user')

    @cached_property
    def my_organisation_roles_for_this_portal(self):
        return self.profile.all_organisation_roles(self.portal)

class EditProfileView(FormView):
    """
    Straightforward view which displays a form to have a user
    edit his / her own profile.
    """
    template_name = 'lizard_auth_server/edit_profile.html'
    form_class = forms.EditProfileForm
    _profile = None

    @method_decorator(login_required)
    def dispatch(self, request, *args, **kwargs):
        return super(EditProfileView, self).dispatch(request, *args, **kwargs)

    @property
    def profile(self):
        if not self._profile:
            # User.get_profile is deprecated since Django 1.7
            # self._profile = self.request.user.get_profile()
            self._profile = self.request.user.user_profile
        return self._profile

    def get_initial(self):
        return {
            'email': self.profile.email,
            'first_name': self.profile.first_name,
            'last_name': self.profile.last_name,
            'title': self.profile.title,
            'street': self.profile.street,
            'postal_code': self.profile.postal_code,
            'town': self.profile.town,
            'phone_number': self.profile.phone_number,
            'mobile_phone_number': self.profile.mobile_phone_number
        }

    def get_form(self, form_class):
        return form_class(user=self.request.user, **self.get_form_kwargs())

    def form_valid(self, form):
        data = form.cleaned_data

        # let the model handle the rest
        self.profile.update_all(data)

        return HttpResponseRedirect(reverse('profile'))

class InviteUserView(StaffOnlyMixin, FormView):
    template_name = 'lizard_auth_server/invite_user.html'
    form_class = forms.InviteUserForm

    def form_valid(self, form):
        data = form.cleaned_data

        # create and fill a new Invitation
        inv = Invitation()
        inv.name = data['name']
        inv.email = data['email']
        inv.language = data['language']
        inv.organisation = data['organisation']
        inv.save()

        # many-to-many, so save these after the invitation has been
        # assigned an ID
        inv.portals = data['portals']
        inv.save()

        inv.send_new_activation_email()

        return HttpResponseRedirect(
            reverse(
                'lizard_auth_server.invite_user_complete',
                kwargs={'invitation_pk': inv.pk}))

class InviteUserCompleteView(StaffOnlyMixin, TemplateView):
    template_name = 'lizard_auth_server/invite_user_complete.html'

    def get(self, request, invitation_pk, *args, **kwargs):
        self.invitation_pk = int(invitation_pk)
        return super(
            InviteUserCompleteView, self).get(request, *args, **kwargs)

    @cached_property
    def invitiation(self):
        return Invitation.objects.get(pk=self.invitation_pk)

class InvitationMixin(object):
    invitation = None
    activation_key = None
    error_on_already_used = True

    def dispatch(self, request, activation_key, *args, **kwargs):
        self.activation_key = activation_key
        try:
            self.invitation = Invitation.objects.get(
                activation_key=self.activation_key)
        except Invitation.DoesNotExist:
            return self.invalid_activation_key(request)

        # show a semi-nice error page if the invitation was already used
        if self.error_on_already_used and self.invitation.is_activated:
            return self.invalid_activation_key(request)

        return super(InvitationMixin, self).dispatch(request, *args, **kwargs)

    def invalid_activation_key(self, request):
        logger.warn(
            'invalid activation key used by %s',
            request.META['REMOTE_ADDR'])
        return ErrorMessageResponse(
            request,
            _('Invalid activation key. Perhaps this account '
              'was already activated?'), 404)

class ActivateUserView1(InvitationMixin, FormView):
    template_name = 'lizard_auth_server/activate_user.html'
    form_class = forms.ActivateUserForm1

    def form_valid(self, form):
        data = form.cleaned_data

        # let the model handle the rest
        self.invitation.create_user(data)

        return HttpResponseRedirect(
            reverse(
                'lizard_auth_server.activate_step_2',
                kwargs={'activation_key': self.activation_key}))

class ActivateUserView2(InvitationMixin, FormView):
    template_name = 'lizard_auth_server/activate_user_step_2.html'
    form_class = forms.EditProfileForm

    def get_initial(self):
        return {
            'email': self.invitation.email,
        }

    def form_valid(self, form):
        data = form.cleaned_data

        # let the model handle the rest
        self.invitation.activate(data)

        return HttpResponseRedirect(
            reverse(
                'lizard_auth_server.activation_complete',
                kwargs={'activation_key': self.activation_key}))

class ActivationCompleteView(InvitationMixin, TemplateView):
    template_name = 'lizard_auth_server/activation_complete.html'
    error_on_already_used = False  # see InvitationMixin
    _profile = None

    @property
    def profile(self):
        if not self._profile:
            # User.get_profile is deprecated since Django 1.7
            # self._profile = self.invitation.user.get_profile()
            self._profile = self.invitation.user.user_profile
        return self._profile

class JWTView(View):
    """A view for authenticating to an SSO-managed portal with a JSON Web Token.

    Anonymous users will first be redirected to a login form. Authenticated
    users will receive a (new) token right away.

    The query string may have a `next` parameter used for redirection. If
    present, it must be a fully qualified URL starting with http(s). The
    token will be attached to the query string as an extra parameter:
    <next>?foo=bar&access_token=xxxxx.yyyyy.zzzzz&...

    If a `next` parameter is absent, a JSON response is returned:

    {"access_token": "xxxxx.yyyyy.zzzzz"}

    """

    @staticmethod
    def is_url(url):
        """Validate a URL.

        The current validator accepts only absolute URLs, which makes
        sense as the token is to be generated for another portal.

        Args:
            url (str): an absolute, i.e. fully qualified, URL.

        Returns:
            bool: True if valid, False otherwise.

        """
        val = URLValidator()
        try:
            val(url)
            return True
        except ValidationError:
            return False

    @staticmethod
    def is_portal(sso_key):
        """Validate a portal.

        Args:
            sso_key (str): a public key identifying the portal.

        Returns:
            bool: True if the portal exists, False otherwise.

        """
        return Portal.objects.filter(sso_key=sso_key).exists()

    @staticmethod
    def get_token(user, portal, exp=None):
        """Return a JSON Web Token.

        Args:
            user (django.contrib.auth.models.User): an active, authenticated
                user that has access to the portal.
            portal (lizard_auth_server.models.Portal): a Portal instance.
            exp (int/datetime): expiration time - in UTC - on or after
                the token must not be accepted for processing.

        Returns:
            str: A JSON Web Token.

        """
        assert(user.is_active and user.is_authenticated())
        assert(user.user_profile.has_access(portal))
        if exp is None:
            exp = datetime.utcnow() + JWT_EXPIRATION_DELTA
        payload = {'exp': exp, 'username': user.username}
        secret = portal.sso_secret
        token = jwt.encode(payload, secret, algorithm=JWT_ALGORITHM)
        return token

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super(JWTView, self).dispatch(*args, **kwargs)

    def get(self, request, *args, **kwargs):
        """Return a token.

        If the request has a `next` query string parameter, the response will
        be a redirect (302) and the token is added to the `next` URL. If a
        `next` parameter is absent, a JSON response is returned.

        An HTTP 400 Bad Request is returned in case of trouble.

        """
        self.redirect_to = request.GET.get('next', '')
        sso_key = request.GET.get('portal', '')

        if self.redirect_to and not JWTView.is_url(self.redirect_to):
            reason = _('Invalid `next` query string parameter.')
            return HttpResponseBadRequest(reason, content_type='text/plain')

        if not sso_key:
            reason = _('Missing `portal` query string parameter.')
            return HttpResponseBadRequest(reason, content_type='text/plain')

        if not JWTView.is_portal(sso_key):
            reason = _('Invalid `portal` query string parameter.')
            return HttpResponseBadRequest(reason, content_type='text/plain')

        portal = Portal.objects.get(sso_key=sso_key)

        if not request.user.user_profile.has_access(portal):
            reason = _('You do not have access to this portal.')
            return HttpResponseBadRequest(reason, content_type='text/plain')

        self.token = JWTView.get_token(request.user, portal)

        if self.redirect_to:
            return redirect(self.success_url)
        else:
            return HttpResponse(self.token, content_type='text/plain')

    @property
    def success_url(self):
        """Return the `next` URL with a token as query string parameter."""
        # URL to parts.
        scheme, netloc, path, params, query, fragment = parse.urlparse(
            self.redirect_to)
        # Add (extra) query string parameter.
        data = parse.parse_qs(query)
        data['access_token'] = self.token
        query = parse.urlencode(data, True)
        # Parts to URL.
        url = parse.urlunparse((
            scheme, netloc, path, params, query, fragment))
        return str(url)
<commit_msg>zapped comments<commit_after># -*- coding: utf-8 -*-

from __future__ import unicode_literals

from datetime import datetime
import jwt
import logging

from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.core.validators import URLValidator
from django.http import HttpResponse
from django.http import HttpResponseBadRequest
from django.http import HttpResponseRedirect
from django.shortcuts import redirect
from django.template.context import RequestContext
from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator
from django.utils.functional import cached_property
from django.utils.translation import ugettext as _
from django.views.generic import View
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView

# urllib is the hardest module to use from Python 2/3 compatible code. Six
# provides a version for wrapping over differences between Python 2 and 3.
from six.moves.urllib import parse

from lizard_auth_server import forms
from lizard_auth_server.conf import settings
from lizard_auth_server.models import Invitation
from lizard_auth_server.models import Portal

JWT_ALGORITHM = settings.LIZARD_AUTH_SERVER_JWT_ALGORITHM
JWT_EXPIRATION_DELTA = settings.LIZARD_AUTH_SERVER_JWT_EXPIRATION_DELTA

logger = logging.getLogger(__name__)

class StaffOnlyMixin(object):
    """
    Ensures access by staff members (user.is_staff is True) only to all
    HTTP methods.

    Ensure this is first in the inheritance list!
    """
    @method_decorator(staff_member_required)
    def dispatch(self, request, *args, **kwargs):
        return super(StaffOnlyMixin, self).dispatch(request, *args, **kwargs)

class ErrorMessageResponse(TemplateResponse):
    """
    Display a slightly more user-friendly error message.
    """
    def __init__(self, request, error_message=None, status=500):
        if not error_message:
            error_message = _('An unknown error occurred.')
        context = RequestContext(
            request,
            {
                'error_message': error_message
            }
        )
        super(ErrorMessageResponse, self).__init__(
            request,
            'lizard_auth_server/error_message.html',
            context,
            status=status
        )

##################################################
# Invitation / registration / activation / profile
##################################################

class ProfileView(TemplateView):
    """
    Straightforward view which displays a user's profile.
    """
    template_name = 'lizard_auth_server/profile.html'

    @cached_property
    def profile(self):
        return self.request.user.user_profile

    @property
    def portals(self):
        if self.request.user.is_staff:
            return Portal.objects.all()
        else:
            return self.profile.portals.all()

    @method_decorator(login_required)
    def dispatch(self, request, *args, **kwargs):
        return super(ProfileView, self).dispatch(request, *args, **kwargs)

class AccessToPortalView(TemplateView):
    template_name = 'lizard_auth_server/access-to-portal.html'

    @method_decorator(login_required)
    def dispatch(self, request, *args, **kwargs):
        return super(AccessToPortalView, self).dispatch(
            request, *args, **kwargs)

    @cached_property
    def portal(self):
        portal_pk = self.kwargs['portal_pk']
        return Portal.objects.get(id=portal_pk)

    @cached_property
    def title(self):
        return _('Access to portal {} for {}').format(self.portal.name,
                                                      self.profile)

    @cached_property
    def profile(self):
        if self.request.user.is_staff:
            user_id = self.kwargs.get('user_pk')
            if user_id:
                user = User.objects.get(id=user_id)
                return user.user_profile
        return self.request.user.user_profile

    @cached_property
    def organisation_roles_explanation(self):
        if not self.request.user.is_staff:
            return
        return self.profile.all_organisation_roles(
            self.portal,
            return_explanation=True)

    @cached_property
    def user_profiles_for_portal(self):
        if not self.request.user.is_staff:
            return
        return self.portal.user_profiles.select_related('user')

    @cached_property
    def my_organisation_roles_for_this_portal(self):
        return self.profile.all_organisation_roles(self.portal)

class EditProfileView(FormView):
    """
    Straightforward view which displays a form to have a user
    edit his / her own profile.
    """
    template_name = 'lizard_auth_server/edit_profile.html'
    form_class = forms.EditProfileForm
    _profile = None

    @method_decorator(login_required)
    def dispatch(self, request, *args, **kwargs):
        return super(EditProfileView, self).dispatch(request, *args, **kwargs)

    @property
    def profile(self):
        if not self._profile:
            self._profile = self.request.user.user_profile
        return self._profile

    def get_initial(self):
        return {
            'email': self.profile.email,
            'first_name': self.profile.first_name,
            'last_name': self.profile.last_name,
            'title': self.profile.title,
            'street': self.profile.street,
            'postal_code': self.profile.postal_code,
            'town': self.profile.town,
            'phone_number': self.profile.phone_number,
            'mobile_phone_number': self.profile.mobile_phone_number
        }

    def get_form(self, form_class):
        return form_class(user=self.request.user, **self.get_form_kwargs())

    def form_valid(self, form):
        data = form.cleaned_data

        # let the model handle the rest
        self.profile.update_all(data)

        return HttpResponseRedirect(reverse('profile'))

class InviteUserView(StaffOnlyMixin, FormView):
    template_name = 'lizard_auth_server/invite_user.html'
    form_class = forms.InviteUserForm

    def form_valid(self, form):
        data = form.cleaned_data

        # create and fill a new Invitation
        inv = Invitation()
        inv.name = data['name']
        inv.email = data['email']
        inv.language = data['language']
        inv.organisation = data['organisation']
        inv.save()

        # many-to-many, so save these after the invitation has been
        # assigned an ID
        inv.portals = data['portals']
        inv.save()

        inv.send_new_activation_email()

        return HttpResponseRedirect(
            reverse(
                'lizard_auth_server.invite_user_complete',
                kwargs={'invitation_pk': inv.pk}))

class InviteUserCompleteView(StaffOnlyMixin, TemplateView):
    template_name = 'lizard_auth_server/invite_user_complete.html'

    def get(self, request, invitation_pk, *args, **kwargs):
        self.invitation_pk = int(invitation_pk)
        return super(
            InviteUserCompleteView, self).get(request, *args, **kwargs)

    @cached_property
    def invitiation(self):
        return Invitation.objects.get(pk=self.invitation_pk)

class InvitationMixin(object):
    invitation = None
    activation_key = None
    error_on_already_used = True

    def dispatch(self, request, activation_key, *args, **kwargs):
        self.activation_key = activation_key
        try:
            self.invitation = Invitation.objects.get(
                activation_key=self.activation_key)
        except Invitation.DoesNotExist:
            return self.invalid_activation_key(request)

        # show a semi-nice error page if the invitation was already used
        if self.error_on_already_used and self.invitation.is_activated:
            return self.invalid_activation_key(request)

        return super(InvitationMixin, self).dispatch(request, *args, **kwargs)

    def invalid_activation_key(self, request):
        logger.warn(
            'invalid activation key used by %s',
            request.META['REMOTE_ADDR'])
        return ErrorMessageResponse(
            request,
            _('Invalid activation key. Perhaps this account '
              'was already activated?'), 404)

class ActivateUserView1(InvitationMixin, FormView):
    template_name = 'lizard_auth_server/activate_user.html'
    form_class = forms.ActivateUserForm1

    def form_valid(self, form):
        data = form.cleaned_data

        # let the model handle the rest
        self.invitation.create_user(data)

        return HttpResponseRedirect(
            reverse(
                'lizard_auth_server.activate_step_2',
                kwargs={'activation_key': self.activation_key}))

class ActivateUserView2(InvitationMixin, FormView):
    template_name = 'lizard_auth_server/activate_user_step_2.html'
    form_class = forms.EditProfileForm

    def get_initial(self):
        return {
            'email': self.invitation.email,
        }

    def form_valid(self, form):
        data = form.cleaned_data

        # let the model handle the rest
        self.invitation.activate(data)

        return HttpResponseRedirect(
            reverse(
                'lizard_auth_server.activation_complete',
                kwargs={'activation_key': self.activation_key}))

class ActivationCompleteView(InvitationMixin, TemplateView):
    template_name = 'lizard_auth_server/activation_complete.html'
    error_on_already_used = False  # see InvitationMixin
    _profile = None

    @property
    def profile(self):
        if not self._profile:
            # User.get_profile is deprecated since Django 1.7
            # self._profile = self.invitation.user.get_profile()
            self._profile = self.invitation.user.user_profile
        return self._profile

class JWTView(View):
    """A view for authenticating to an SSO-managed portal with a JSON Web Token.

    Anonymous users will first be redirected to a login form. Authenticated
    users will receive a (new) token right away.

    The query string may have a `next` parameter used for redirection. If
    present, it must be a fully qualified URL starting with http(s). The
    token will be attached to the query string as an extra parameter:
    <next>?foo=bar&access_token=xxxxx.yyyyy.zzzzz&...

    If a `next` parameter is absent, a JSON response is returned:

    {"access_token": "xxxxx.yyyyy.zzzzz"}

    """

    @staticmethod
    def is_url(url):
        """Validate a URL.

        The current validator accepts only absolute URLs, which makes
        sense as the token is to be generated for another portal.

        Args:
            url (str): an absolute, i.e. fully qualified, URL.

        Returns:
            bool: True if valid, False otherwise.

        """
        val = URLValidator()
        try:
            val(url)
            return True
        except ValidationError:
            return False

    @staticmethod
    def is_portal(sso_key):
        """Validate a portal.

        Args:
            sso_key (str): a public key identifying the portal.

        Returns:
            bool: True if the portal exists, False otherwise.

        """
        return Portal.objects.filter(sso_key=sso_key).exists()

    @staticmethod
    def get_token(user, portal, exp=None):
        """Return a JSON Web Token.

        Args:
            user (django.contrib.auth.models.User): an active, authenticated
                user that has access to the portal.
            portal (lizard_auth_server.models.Portal): a Portal instance.
            exp (int/datetime): expiration time - in UTC - on or after
                the token must not be accepted for processing.

        Returns:
            str: A JSON Web Token.

        """
        assert(user.is_active and user.is_authenticated())
        assert(user.user_profile.has_access(portal))
        if exp is None:
            exp = datetime.utcnow() + JWT_EXPIRATION_DELTA
        payload = {'exp': exp, 'username': user.username}
        secret = portal.sso_secret
        token = jwt.encode(payload, secret, algorithm=JWT_ALGORITHM)
        return token

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super(JWTView, self).dispatch(*args, **kwargs)

    def get(self, request, *args, **kwargs):
        """Return a token.

        If the request has a `next` query string parameter, the response will
        be a redirect (302) and the token is added to the `next` URL. If a
        `next` parameter is absent, a JSON response is returned.

        An HTTP 400 Bad Request is returned in case of trouble.

        """
        self.redirect_to = request.GET.get('next', '')
        sso_key = request.GET.get('portal', '')

        if self.redirect_to and not JWTView.is_url(self.redirect_to):
            reason = _('Invalid `next` query string parameter.')
            return HttpResponseBadRequest(reason, content_type='text/plain')

        if not sso_key:
            reason = _('Missing `portal` query string parameter.')
            return HttpResponseBadRequest(reason, content_type='text/plain')

        if not JWTView.is_portal(sso_key):
            reason = _('Invalid `portal` query string parameter.')
            return HttpResponseBadRequest(reason, content_type='text/plain')

        portal = Portal.objects.get(sso_key=sso_key)

        if not request.user.user_profile.has_access(portal):
            reason = _('You do not have access to this portal.')
            return HttpResponseBadRequest(reason, content_type='text/plain')

        self.token = JWTView.get_token(request.user, portal)

        if self.redirect_to:
            return redirect(self.success_url)
        else:
            return HttpResponse(self.token, content_type='text/plain')

    @property
    def success_url(self):
        """Return the `next` URL with a token as query string parameter."""
        # URL to parts.
        scheme, netloc, path, params, query, fragment = parse.urlparse(
            self.redirect_to)
        # Add (extra) query string parameter.
        data = parse.parse_qs(query)
        data['access_token'] = self.token
        query = parse.urlencode(data, True)
        # Parts to URL.
        url = parse.urlunparse((
            scheme, netloc, path, params, query, fragment))
        return str(url)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# IMPORT
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

from distutils.core import setup

# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# SETUP
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

setup(
	name = 'pycrosscall',
	packages = ['pycrosscall'],
	version = '0.0.1',
	description = 'Calling routines in Windows DLLs from Python scripts running under Linux, MacOS or BSD',
	long_description = long_description,
	author = 'Sebastian M. Ernst',
	author_email = 'ernst@pleiszenburg.de',
	url = 'https://github.com/s-m-e/pycrosscall',
	download_url = 'https://github.com/s-m-e/pycrosscall/archive/pycrosscall_0.0.1.tar.gz',
	license = 'GPLv2',
	keywords = ['ctypes', 'wine'],
	install_requires = [],
	classifiers = [
		'Development Status :: 3 - Alpha',
		'Intended Audience :: Developers',
		'Intended Audience :: Information Technology',
		'Intended Audience :: Science/Research',
		'Intended Audience :: System Administrators',
		'License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)',
		'Operating System :: MacOS',
		'Operating System :: POSIX :: BSD',
		'Operating System :: POSIX :: Linux',
		'Programming Language :: Python :: 3',
		'Programming Language :: Python :: 3 :: Only',
		'Programming Language :: Python :: Implementation :: CPython',
		'Topic :: Scientific/Engineering',
		'Topic :: Software Development',
		'Topic :: System :: Operating System',
		'Topic :: System :: Operating System Kernels',
		'Topic :: Utilities'
		]
)
<commit_msg>pointed to scripts in setup.py<commit_after># -*- coding: utf-8 -*-

# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# IMPORT
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

from distutils.core import setup
import os
from glob import glob

# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# SETUP
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

setup(
	name = 'pycrosscall',
	packages = ['pycrosscall'],
	version = '0.0.1',
	description = 'Calling routines in Windows DLLs from Python scripts running under Linux, MacOS or BSD',
	long_description = long_description,
	author = 'Sebastian M. Ernst',
	author_email = 'ernst@pleiszenburg.de',
	url = 'https://github.com/s-m-e/pycrosscall',
	download_url = 'https://github.com/s-m-e/pycrosscall/archive/pycrosscall_0.0.1.tar.gz',
	license = 'GPLv2',
	keywords = ['ctypes', 'wine'],
	install_requires = [],
	scripts = glob(os.path.join('scripts', '*')),
	classifiers = [
		'Development Status :: 3 - Alpha',
		'Intended Audience :: Developers',
		'Intended Audience :: Information Technology',
		'Intended Audience :: Science/Research',
		'Intended Audience :: System Administrators',
		'License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)',
		'Operating System :: MacOS',
		'Operating System :: POSIX :: BSD',
		'Operating System :: POSIX :: Linux',
		'Programming Language :: Python :: 3',
		'Programming Language :: Python :: 3 :: Only',
		'Programming Language :: Python :: Implementation :: CPython',
		'Topic :: Scientific/Engineering',
		'Topic :: Software Development',
		'Topic :: System :: Operating System',
		'Topic :: System :: Operating System Kernels',
		'Topic :: Utilities'
		]
)
<|endoftext|>


<commit_before>from setuptools import find_packages
from setuptools import setup

setup(name='cleverhans',
      version='4.0.0',
      url='https://github.com/cleverhans-lab/cleverhans',
      license='MIT',
      install_requires=[
          'nose',
          'pycodestyle',
          'scipy',
          'matplotlib',
          "mnist",
          "numpy",
          "tensorflow-probability",
          "joblib",
      ],
      extras_require={
          "jax": ["jax>=0.2.9"],
          "tf": ["tensorflow>=2.4.0"],
          "pytorch": ["torch>=1.7.0", "torchvision>=0.8.0"],
      },
      packages=find_packages())
<commit_msg>Updated setup.py to add jaxlib requirement<commit_after>from setuptools import find_packages
from setuptools import setup

setup(name='cleverhans',
      version='4.0.0',
      url='https://github.com/cleverhans-lab/cleverhans',
      license='MIT',
      install_requires=[
          'nose',
          'pycodestyle',
          'scipy',
          'matplotlib',
          "mnist",
          "numpy",
          "tensorflow-probability",
          "joblib",
      ],
      extras_require={
          "jax": ["jax>=0.2.9", "jaxlib"],
          "tf": ["tensorflow>=2.4.0", "tensorflow-probability"],
          "pytorch": ["torch>=1.7.0", "torchvision>=0.8.0"],
      },
      packages=find_packages())
<|endoftext|>


<commit_before>from setuptools import setup

setup(
    name='pyingest',
    version='0.5.0',
    packages=['pyingest','pyingest.config','pyingest.extractors','pyingest.parsers','pyingest.serializers','pyingest.validators','pyingest.extractors.grobid'],
    install_requires=['git+git://github.com/adsabs/ADSPipelineUtils.git@master', 'bs4<=0.0.1', 'lxml<=4.2.1', 'functools32<=3.2.3.post2', 'jsonschema<=2.6.0', 'namedentities<=1.9.4', 'python-dateutil<=2.6.0', 'six<=1.11.0', 'xmltodict<=0.11.0']
)
<commit_msg> 	modified:   setup.py<commit_after>from setuptools import setup

setup(
    name='pyingest',
    version='0.5.0',
    packages=['pyingest','pyingest.config','pyingest.extractors','pyingest.parsers','pyingest.serializers','pyingest.validators','pyingest.extractors.grobid'],
    install_requires=['git+git://github.com/adsabs/ADSPipelineUtils.git', 'bs4<=0.0.1', 'lxml<=4.2.1', 'functools32<=3.2.3.post2', 'jsonschema<=2.6.0', 'namedentities<=1.9.4', 'python-dateutil<=2.6.0', 'six<=1.11.0', 'xmltodict<=0.11.0']
)
<|endoftext|>


<commit_before># Copyright (C) 2011-2013 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
#
# See the License for the specific language governing permissions and
# limitations under the License.

import concurrent.futures
import sys
import threading
import time

import keystoneauth1.exceptions
import six

import openstack._log
from openstack import exceptions

_log = openstack._log.setup_logging('openstack.task_manager')

class Task(object):
    """Represent a remote task to be performed on an OpenStack Cloud.

    Some consumers need to inject things like rate-limiting or auditing
    around each external REST interaction. Task provides an interface
    to encapsulate each such interaction. Also, although shade itself
    operates normally in a single-threaded direct action manner, consuming
    programs may provide a multi-threaded TaskManager themselves. For that
    reason, Task uses threading events to ensure appropriate wait conditions.
    These should be a no-op in single-threaded applications.

    A consumer is expected to overload the main method.

    :param dict kw: Any args that are expected to be passed to something in
                    the main payload at execution time.
    """

    def __init__(self, main=None, name=None, run_async=False, *args, **kwargs):
        self._exception = None
        self._traceback = None
        self._result = None
        self._response = None
        self._finished = threading.Event()
        self._main = main
        self._run_async = run_async
        self.args = args
        self.kwargs = kwargs
        self.name = name or type(self).__name__

    def main(self):
        return self._main(*self.args, **self.kwargs)

    @property
    def run_async(self):
        return self._run_async

    def done(self, result):
        self._result = result
        self._finished.set()

    def exception(self, e, tb):
        self._exception = e
        self._traceback = tb
        self._finished.set()

    def wait(self, raw=False):
        self._finished.wait()

        if self._exception:
            six.reraise(type(self._exception), self._exception,
                        self._traceback)

        return self._result

    def run(self):
        try:
            # Retry one time if we get a retriable connection failure
            try:
                self.done(self.main())
            except keystoneauth1.exceptions.RetriableConnectionFailure as e:
                self.done(self.main())
        except Exception as e:
            self.exception(e, sys.exc_info()[2])

class TaskManager(object):

    def __init__(self, name, log=_log, workers=5, **kwargs):
        self.name = name
        self._executor = None
        self._log = log
        self._workers = workers

    @property
    def executor(self):
        if not self._executor:
            self._executor = concurrent.futures.ThreadPoolExecutor(
                max_workers=self._workers)
        return self._executor

    def stop(self):
        """ This is a direct action passthrough TaskManager """
        if self._executor:
            self._executor.shutdown()

    def run(self):
        """ This is a direct action passthrough TaskManager """
        pass

    def submit_task(self, task):
        """Submit and execute the given task.

        :param task: The task to execute.
        :param bool raw: If True, return the raw result as received from the
            underlying client call.
        """
        return self.run_task(task=task)

    def submit_function(self, method, name=None, *args, **kwargs):
        """ Allows submitting an arbitrary method for work.

        :param method: Callable to run in the TaskManager.
        :param str name: Name to use for the generated Task object.
        :param args: positional arguments to pass to the method when it runs.
        :param kwargs: keyword arguments to pass to the method when it runs.
        """
        task = Task(main=method, name=name, *args, **kwargs)
        return self.submit_task(task)

    def submit_function_async(self, method, name=None, *args, **kwargs):
        """ Allows submitting an arbitrary method for async work scheduling.

        :param method: Callable to run in the TaskManager.
        :param str name: Name to use for the generated Task object.
        :param args: positional arguments to pass to the method when it runs.
        :param kwargs: keyword arguments to pass to the method when it runs.
        """
        task = Task(method=method, name=name, run_async=True, **kwargs)
        return self.submit_task(task)

    def pre_run_task(self, task):
        self._log.debug(
            "Manager %s running task %s", self.name, task.name)

    def run_task(self, task):
        if task.run_async:
            return self._run_task_async(task)
        else:
            return self._run_task(task)

    def post_run_task(self, elapsed_time, task):
        self._log.debug(
            "Manager %s ran task %s in %ss",
            self.name, task.name, elapsed_time)

    def _run_task_async(self, task):
        self._log.debug(
            "Manager %s submitting task %s", self.name, task.name)
        return self.executor.submit(self._run_task, task)

    def _run_task(self, task):
        self.pre_run_task(task)
        start = time.time()
        task.run()
        end = time.time()
        dt = end - start
        self.post_run_task(dt, task)

        return task.wait()

def wait_for_futures(futures, raise_on_error=True, log=_log):
    '''Collect results or failures from a list of running future tasks.'''

    results = []
    retries = []

    # Check on each result as its thread finishes
    for completed in concurrent.futures.as_completed(futures):
        try:
            result = completed.result()
            exceptions.raise_from_response(result)
            results.append(result)
        except (keystoneauth1.exceptions.RetriableConnectionFailure,
                exceptions.HttpException) as e:
            log.exception(
                "Exception processing async task: {e}".format(e=str(e)))
            if raise_on_error:
                raise
            # If we get an exception, put the result into a list so we
            # can try again
            retries.append(result)
    return results, retries
<commit_msg>Fix wait for futures append 'result' error<commit_after># Copyright (C) 2011-2013 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
#
# See the License for the specific language governing permissions and
# limitations under the License.

import concurrent.futures
import sys
import threading
import time

import keystoneauth1.exceptions
import six

import openstack._log
from openstack import exceptions

_log = openstack._log.setup_logging('openstack.task_manager')

class Task(object):
    """Represent a remote task to be performed on an OpenStack Cloud.

    Some consumers need to inject things like rate-limiting or auditing
    around each external REST interaction. Task provides an interface
    to encapsulate each such interaction. Also, although shade itself
    operates normally in a single-threaded direct action manner, consuming
    programs may provide a multi-threaded TaskManager themselves. For that
    reason, Task uses threading events to ensure appropriate wait conditions.
    These should be a no-op in single-threaded applications.

    A consumer is expected to overload the main method.

    :param dict kw: Any args that are expected to be passed to something in
                    the main payload at execution time.
    """

    def __init__(self, main=None, name=None, run_async=False, *args, **kwargs):
        self._exception = None
        self._traceback = None
        self._result = None
        self._response = None
        self._finished = threading.Event()
        self._main = main
        self._run_async = run_async
        self.args = args
        self.kwargs = kwargs
        self.name = name or type(self).__name__

    def main(self):
        return self._main(*self.args, **self.kwargs)

    @property
    def run_async(self):
        return self._run_async

    def done(self, result):
        self._result = result
        self._finished.set()

    def exception(self, e, tb):
        self._exception = e
        self._traceback = tb
        self._finished.set()

    def wait(self, raw=False):
        self._finished.wait()

        if self._exception:
            six.reraise(type(self._exception), self._exception,
                        self._traceback)

        return self._result

    def run(self):
        try:
            # Retry one time if we get a retriable connection failure
            try:
                self.done(self.main())
            except keystoneauth1.exceptions.RetriableConnectionFailure as e:
                self.done(self.main())
        except Exception as e:
            self.exception(e, sys.exc_info()[2])

class TaskManager(object):

    def __init__(self, name, log=_log, workers=5, **kwargs):
        self.name = name
        self._executor = None
        self._log = log
        self._workers = workers

    @property
    def executor(self):
        if not self._executor:
            self._executor = concurrent.futures.ThreadPoolExecutor(
                max_workers=self._workers)
        return self._executor

    def stop(self):
        """ This is a direct action passthrough TaskManager """
        if self._executor:
            self._executor.shutdown()

    def run(self):
        """ This is a direct action passthrough TaskManager """
        pass

    def submit_task(self, task):
        """Submit and execute the given task.

        :param task: The task to execute.
        :param bool raw: If True, return the raw result as received from the
            underlying client call.
        """
        return self.run_task(task=task)

    def submit_function(self, method, name=None, *args, **kwargs):
        """ Allows submitting an arbitrary method for work.

        :param method: Callable to run in the TaskManager.
        :param str name: Name to use for the generated Task object.
        :param args: positional arguments to pass to the method when it runs.
        :param kwargs: keyword arguments to pass to the method when it runs.
        """
        task = Task(main=method, name=name, *args, **kwargs)
        return self.submit_task(task)

    def submit_function_async(self, method, name=None, *args, **kwargs):
        """ Allows submitting an arbitrary method for async work scheduling.

        :param method: Callable to run in the TaskManager.
        :param str name: Name to use for the generated Task object.
        :param args: positional arguments to pass to the method when it runs.
        :param kwargs: keyword arguments to pass to the method when it runs.
        """
        task = Task(method=method, name=name, run_async=True, **kwargs)
        return self.submit_task(task)

    def pre_run_task(self, task):
        self._log.debug(
            "Manager %s running task %s", self.name, task.name)

    def run_task(self, task):
        if task.run_async:
            return self._run_task_async(task)
        else:
            return self._run_task(task)

    def post_run_task(self, elapsed_time, task):
        self._log.debug(
            "Manager %s ran task %s in %ss",
            self.name, task.name, elapsed_time)

    def _run_task_async(self, task):
        self._log.debug(
            "Manager %s submitting task %s", self.name, task.name)
        return self.executor.submit(self._run_task, task)

    def _run_task(self, task):
        self.pre_run_task(task)
        start = time.time()
        task.run()
        end = time.time()
        dt = end - start
        self.post_run_task(dt, task)

        return task.wait()

def wait_for_futures(futures, raise_on_error=True, log=_log):
    '''Collect results or failures from a list of running future tasks.'''

    results = []
    retries = []

    # Check on each result as its thread finishes
    for completed in concurrent.futures.as_completed(futures):
        try:
            result = completed.result()
            exceptions.raise_from_response(result)
            results.append(result)
        except (keystoneauth1.exceptions.RetriableConnectionFailure,
                exceptions.HttpException) as e:
            log.exception(
                "Exception processing async task: {e}".format(e=str(e)))
            if raise_on_error:
                raise
            # If we get an exception, put the result into a list so we
            # can try again
            retries.append(completed.result())
    return results, retries
<|endoftext|>


<commit_before>import os
from setuptools import setup, find_packages

README = open(os.path.join(os.path.dirname(__file__), "README.rst")).read()

os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))

setup(
    name="recipe_scrapers",
    url="https://github.com/hhursev/recipe-scrapers/",
    version="5.8.1",
    author="Hristo Harsev",
    author_email="r+pypi@hharsev.com",
    description="Python package, scraping recipes from all over the internet",
    keywords="python recipes scraper harvest recipe-scraper recipe-scrapers",
    long_description=README,
    install_requires=[
        "beautifulsoup4>=4.6.0",
        "requests>=2.19.1",
    ],
    packages=find_packages(),
    package_data={"": ["LICENSE"]},
    include_package_data=True,
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Intended Audience :: Developers",
        "Operating System :: OS Independent",
        'Topic :: Internet :: WWW/HTTP',
    ],
    python_requires='>=3.4'
)
<commit_msg>Bump version to 5.14.0<commit_after>import os
from setuptools import setup, find_packages

README = open(os.path.join(os.path.dirname(__file__), "README.rst")).read()

os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))

setup(
    name="recipe_scrapers",
    url="https://github.com/hhursev/recipe-scrapers/",
    version="5.14.0",
    author="Hristo Harsev",
    author_email="r+pypi@hharsev.com",
    description="Python package, scraping recipes from all over the internet",
    keywords="python recipes scraper harvest recipe-scraper recipe-scrapers",
    long_description=README,
    install_requires=[
        "beautifulsoup4>=4.6.0",
        "requests>=2.19.1",
    ],
    packages=find_packages(),
    package_data={"": ["LICENSE"]},
    include_package_data=True,
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Intended Audience :: Developers",
        "Operating System :: OS Independent",
        'Topic :: Internet :: WWW/HTTP',
    ],
    python_requires='>=3.4'
)
<|endoftext|>