<commit_before># coding=utf-8
from __future__ import absolute_import

__author__ = "Gina Häußge <osd@foosel.net>"
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
__copyright__ = "Copyright (C) 2015 The OctoPrint Project - Released under terms of the AGPLv3 License"

import click

class OctoPrintDevelCommands(click.MultiCommand):
	"""
	Custom `click.MultiCommand <http://click.pocoo.org/5/api/#click.MultiCommand>`_
	implementation that provides commands relevant for (plugin) development
	based on availability of development dependencies.
	"""

	sep = ":"
	groups = ("plugin",)

	def __init__(self, *args, **kwargs):
		click.MultiCommand.__init__(self, *args, **kwargs)

		from octoprint.util.commandline import CommandlineCaller
		from functools import partial

		def log_util(f):
			def log(*lines):
					for line in lines:
						f(line)
			return log

		self.command_caller = CommandlineCaller()
		self.command_caller.on_log_call = log_util(lambda x: click.echo(">> {}".format(x)))
		self.command_caller.on_log_stdout = log_util(click.echo)
		self.command_caller.on_log_stderr = log_util(partial(click.echo, err=True))

	def _get_prefix_methods(self, method_prefix):
		for name in [x for x in dir(self) if x.startswith(method_prefix)]:
			method = getattr(self, name)
			yield method

	def _get_commands_from_prefix_methods(self, method_prefix):
		for method in self._get_prefix_methods(method_prefix):
			result = method()
			if result is not None and isinstance(result, click.Command):
				yield result

	def _get_commands(self):
		result = dict()
		for group in self.groups:
			for command in self._get_commands_from_prefix_methods("{}_".format(group)):
				result[group + self.sep + command.name] = command
		return result

	def list_commands(self, ctx):
		result = [name for name in self._get_commands()]
		result.sort()
		return result

	def get_command(self, ctx, cmd_name):
		commands = self._get_commands()
		return commands.get(cmd_name, None)

	def plugin_new(self):
		try:
			import cookiecutter.main
		except ImportError:
			return None

		import contextlib

		@contextlib.contextmanager
		def custom_cookiecutter_config(config):
			"""
			Allows overriding cookiecutter's user config with a custom dict
			with fallback to the original data.
			"""
			from octoprint.util import fallback_dict

			original_get_user_config = cookiecutter.main.get_user_config
			try:
				def f(*args, **kwargs):
					original_config = original_get_user_config(*args, **kwargs)
					return fallback_dict(config, original_config)
				cookiecutter.main.get_user_config = f
				yield
			finally:
				cookiecutter.main.get_user_config = original_get_user_config

		@contextlib.contextmanager
		def custom_cookiecutter_prompt(options):
			"""
			Custom cookiecutter prompter for the template config.

			If a setting is available in the provided options (read from the CLI)
			that will be used, otherwise the user will be prompted for a value
			via click.
			"""
			original_prompt_for_config = cookiecutter.main.prompt_for_config

			def custom_prompt_for_config(context, no_input=False):
				import cookiecutter.prompt

				cookiecutter_dict = {}
				env = cookiecutter.prompt.Environment()

				for key, raw in context['cookiecutter'].items():
					if key in options:
						val = options[key]
					else:
						raw = raw if isinstance(raw, basestring) else str(raw)
						val = env.from_string(raw).render(cookiecutter=cookiecutter_dict)

						if not no_input:
							val = click.prompt(key, default=val)

					cookiecutter_dict[key] = val
				return cookiecutter_dict

			try:
				cookiecutter.main.prompt_for_config = custom_prompt_for_config
				yield
			finally:
				cookiecutter.main.prompt_for_config = original_prompt_for_config

		@click.command("new")
		@click.option("--name", "-n", help="The name of the plugin")
		@click.option("--package", "-p", help="The plugin package")
		@click.option("--author", "-a", help="The plugin author's name")
		@click.option("--email", "-e", help="The plugin author's mail address")
		@click.option("--license", "-l", help="The plugin's license")
		@click.option("--description", "-d", help="The plugin's description")
		@click.option("--homepage", help="The plugin's homepage URL")
		@click.option("--source", "-s", help="The URL to the plugin's source")
		@click.option("--installurl", "-i", help="The plugin's install URL")
		@click.argument("identifier", required=False)
		def command(name, package, author, email, description, license, homepage, source, installurl, identifier):
			"""Creates a new plugin based on the OctoPrint Plugin cookiecutter template."""
			from octoprint.util import tempdir

			# deleting a git checkout folder might run into access errors due
			# to write-protected sub folders, so we use a custom onerror handler
			# that tries to fix such permissions
			def onerror(func, path, exc_info):
				"""Originally from http://stackoverflow.com/a/2656405/2028598"""
				import stat
				import os

				if not os.access(path, os.W_OK):
					os.chmod(path, stat.S_IWUSR)
					func(path)
				else:
					raise

			with tempdir(onerror=onerror) as path:
				custom = dict(cookiecutters_dir=path)
				with custom_cookiecutter_config(custom):
					raw_options = dict(
						plugin_identifier=identifier,
						plugin_package=package,
						plugin_name=name,
						full_name=author,
						email=email,
						plugin_description=description,
						plugin_license=license,
						plugin_homepage=homepage,
						plugin_source=source,
						plugin_installurl=installurl
					)
					options = dict((k, v) for k, v in raw_options.items() if v is not None)

					with custom_cookiecutter_prompt(options):
						cookiecutter.main.cookiecutter("gh:OctoPrint/cookiecutter-octoprint-plugin")

		return command

	def plugin_install(self):
		@click.command("install")
		@click.option("--path", help="Path of the local plugin development folder to install")
		def command(path):
			"""
			Installs the local plugin in development mode.

			Note: This can NOT be used to install plugins from remote locations
			such as the plugin repository! It is strictly for local development
			of plugins, to ensure the plugin is installed (editable) into the
			same python environment that OctoPrint is installed under.
			"""

			import os
			import sys

			if not path:
				path = os.getcwd()

			# check if this really looks like a plugin
			if not os.path.isfile(os.path.join(path, "setup.py")):
				click.echo("This doesn't look like an OctoPrint plugin folder")
				sys.exit(1)

			self.command_caller.call([sys.executable, "setup.py", "develop"], cwd=path)

		return command

	def plugin_uninstall(self):
		from octoprint.util.pip import PipCaller
		pip_command = PipCaller.autodetect_pip()
		if pip_command is None:
			return

		@click.command("uninstall")
		@click.argument("name")
		def command(name):
			"""Uninstalls the plugin with the given name."""
			import sys

			lower_name = name.lower()
			if not lower_name.startswith("octoprint_") and not lower_name.startswith("octoprint-"):
				click.echo("This doesn't look like an OctoPrint plugin name")
				sys.exit(1)

			call = [pip_command, "uninstall", "--yes", name]
			self.command_caller.call(call)

		return command

@click.group()
def dev_commands():
	pass

@dev_commands.group(name="dev", cls=OctoPrintDevelCommands)
def dev():
	"""Additional commands for development tasks."""
	pass
<commit_msg>Use [sys.executable, "-m", "pip"] for the CLI pip stuff too<commit_after># coding=utf-8
from __future__ import absolute_import

__author__ = "Gina Häußge <osd@foosel.net>"
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
__copyright__ = "Copyright (C) 2015 The OctoPrint Project - Released under terms of the AGPLv3 License"

import click

class OctoPrintDevelCommands(click.MultiCommand):
	"""
	Custom `click.MultiCommand <http://click.pocoo.org/5/api/#click.MultiCommand>`_
	implementation that provides commands relevant for (plugin) development
	based on availability of development dependencies.
	"""

	sep = ":"
	groups = ("plugin",)

	def __init__(self, *args, **kwargs):
		click.MultiCommand.__init__(self, *args, **kwargs)

		from octoprint.util.commandline import CommandlineCaller
		from functools import partial

		def log_util(f):
			def log(*lines):
					for line in lines:
						f(line)
			return log

		self.command_caller = CommandlineCaller()
		self.command_caller.on_log_call = log_util(lambda x: click.echo(">> {}".format(x)))
		self.command_caller.on_log_stdout = log_util(click.echo)
		self.command_caller.on_log_stderr = log_util(partial(click.echo, err=True))

	def _get_prefix_methods(self, method_prefix):
		for name in [x for x in dir(self) if x.startswith(method_prefix)]:
			method = getattr(self, name)
			yield method

	def _get_commands_from_prefix_methods(self, method_prefix):
		for method in self._get_prefix_methods(method_prefix):
			result = method()
			if result is not None and isinstance(result, click.Command):
				yield result

	def _get_commands(self):
		result = dict()
		for group in self.groups:
			for command in self._get_commands_from_prefix_methods("{}_".format(group)):
				result[group + self.sep + command.name] = command
		return result

	def list_commands(self, ctx):
		result = [name for name in self._get_commands()]
		result.sort()
		return result

	def get_command(self, ctx, cmd_name):
		commands = self._get_commands()
		return commands.get(cmd_name, None)

	def plugin_new(self):
		try:
			import cookiecutter.main
		except ImportError:
			return None

		import contextlib

		@contextlib.contextmanager
		def custom_cookiecutter_config(config):
			"""
			Allows overriding cookiecutter's user config with a custom dict
			with fallback to the original data.
			"""
			from octoprint.util import fallback_dict

			original_get_user_config = cookiecutter.main.get_user_config
			try:
				def f(*args, **kwargs):
					original_config = original_get_user_config(*args, **kwargs)
					return fallback_dict(config, original_config)
				cookiecutter.main.get_user_config = f
				yield
			finally:
				cookiecutter.main.get_user_config = original_get_user_config

		@contextlib.contextmanager
		def custom_cookiecutter_prompt(options):
			"""
			Custom cookiecutter prompter for the template config.

			If a setting is available in the provided options (read from the CLI)
			that will be used, otherwise the user will be prompted for a value
			via click.
			"""
			original_prompt_for_config = cookiecutter.main.prompt_for_config

			def custom_prompt_for_config(context, no_input=False):
				import cookiecutter.prompt

				cookiecutter_dict = {}
				env = cookiecutter.prompt.Environment()

				for key, raw in context['cookiecutter'].items():
					if key in options:
						val = options[key]
					else:
						raw = raw if isinstance(raw, basestring) else str(raw)
						val = env.from_string(raw).render(cookiecutter=cookiecutter_dict)

						if not no_input:
							val = click.prompt(key, default=val)

					cookiecutter_dict[key] = val
				return cookiecutter_dict

			try:
				cookiecutter.main.prompt_for_config = custom_prompt_for_config
				yield
			finally:
				cookiecutter.main.prompt_for_config = original_prompt_for_config

		@click.command("new")
		@click.option("--name", "-n", help="The name of the plugin")
		@click.option("--package", "-p", help="The plugin package")
		@click.option("--author", "-a", help="The plugin author's name")
		@click.option("--email", "-e", help="The plugin author's mail address")
		@click.option("--license", "-l", help="The plugin's license")
		@click.option("--description", "-d", help="The plugin's description")
		@click.option("--homepage", help="The plugin's homepage URL")
		@click.option("--source", "-s", help="The URL to the plugin's source")
		@click.option("--installurl", "-i", help="The plugin's install URL")
		@click.argument("identifier", required=False)
		def command(name, package, author, email, description, license, homepage, source, installurl, identifier):
			"""Creates a new plugin based on the OctoPrint Plugin cookiecutter template."""
			from octoprint.util import tempdir

			# deleting a git checkout folder might run into access errors due
			# to write-protected sub folders, so we use a custom onerror handler
			# that tries to fix such permissions
			def onerror(func, path, exc_info):
				"""Originally from http://stackoverflow.com/a/2656405/2028598"""
				import stat
				import os

				if not os.access(path, os.W_OK):
					os.chmod(path, stat.S_IWUSR)
					func(path)
				else:
					raise

			with tempdir(onerror=onerror) as path:
				custom = dict(cookiecutters_dir=path)
				with custom_cookiecutter_config(custom):
					raw_options = dict(
						plugin_identifier=identifier,
						plugin_package=package,
						plugin_name=name,
						full_name=author,
						email=email,
						plugin_description=description,
						plugin_license=license,
						plugin_homepage=homepage,
						plugin_source=source,
						plugin_installurl=installurl
					)
					options = dict((k, v) for k, v in raw_options.items() if v is not None)

					with custom_cookiecutter_prompt(options):
						cookiecutter.main.cookiecutter("gh:OctoPrint/cookiecutter-octoprint-plugin")

		return command

	def plugin_install(self):
		@click.command("install")
		@click.option("--path", help="Path of the local plugin development folder to install")
		def command(path):
			"""
			Installs the local plugin in development mode.

			Note: This can NOT be used to install plugins from remote locations
			such as the plugin repository! It is strictly for local development
			of plugins, to ensure the plugin is installed (editable) into the
			same python environment that OctoPrint is installed under.
			"""

			import os
			import sys

			if not path:
				path = os.getcwd()

			# check if this really looks like a plugin
			if not os.path.isfile(os.path.join(path, "setup.py")):
				click.echo("This doesn't look like an OctoPrint plugin folder")
				sys.exit(1)

			self.command_caller.call([sys.executable, "setup.py", "develop"], cwd=path)

		return command

	def plugin_uninstall(self):
		@click.command("uninstall")
		@click.argument("name")
		def command(name):
			"""Uninstalls the plugin with the given name."""
			import sys

			lower_name = name.lower()
			if not lower_name.startswith("octoprint_") and not lower_name.startswith("octoprint-"):
				click.echo("This doesn't look like an OctoPrint plugin name")
				sys.exit(1)

			call = [sys.executable, "-m", "pip", "uninstall", "--yes", name]
			self.command_caller.call(call)

		return command

@click.group()
def dev_commands():
	pass

@dev_commands.group(name="dev", cls=OctoPrintDevelCommands)
def dev():
	"""Additional commands for development tasks."""
	pass
<|endoftext|>


<commit_before># The MIT License (MIT)
#
# Copyright (c) 2013 Numenta, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import hashlib
import os
import json
import requests
import urllib

DEFAULT_BASE_URL = "http://s_api.cortical.io:80/rest/"
DEFAULT_RETINA = "eng_syn"
DEFAULT_CACHE_DIR = "/tmp/pycept"
DEFAULT_VERBOSITY = 0

RETINA_SIZES = {
  "eng_syn": {
    "width": 64,
    "height": 64
  },
  "eng_syn_morph": {
    "width": 64,
    "height": 64
  },
  "eng_gen": {
    "width": 128,
    "height": 128
  }
}

class Cept(object):
  """
  Main class for the Cept API.
  """

  def __init__(self, api_key,
               base_url=DEFAULT_BASE_URL,
               retina=DEFAULT_RETINA,
               cache_dir=DEFAULT_CACHE_DIR,
               verbosity=DEFAULT_VERBOSITY):
    self.api_key = api_key
    self.api_url = base_url
    self.retina = retina
    # Create the cache directory if necessary.
    cache_dir = os.path.join(cache_dir, retina)
    if not os.path.exists(cache_dir):
      os.makedirs(cache_dir)
    self.cache_dir = cache_dir
    self.verbosity = verbosity

  def getBitmap(self, term):
    url = self._buildUrl("text")
    # Create a cache location for each term, where it will either be read in from
    # or cached within if we have to go to the CEPT API to get the SDR.
    cache_file = os.path.join(self.cache_dir, term + '.json')
    # Get it from the cache if it's there.
    if os.path.exists(cache_file):
      sdr = json.loads(open(cache_file).read())
    # Get it from CEPT API if it's not cached.
    else:
      if self.verbosity > 0:
        print '\tfetching %s from CEPT API' % term
      headers = {'Content-Type': 'application/json'}
      response = requests.post(url,
                               headers=headers,
                               data=term)
      responseObj = json.loads(response.content)
      if type(responseObj) == list:
        sdr = responseObj[0]
      else:
        sdr = {"positions": []}

      if (not 'width' in sdr) or (not 'height' in sdr):
        size = RETINA_SIZES[self.retina]
        sdr['width'] = size['width']
        sdr['height'] = size['height']

      # attach the sparsity for reference
      total = float(sdr['width']) * float(sdr['height'])
      on = len(sdr['positions'])
      sparsity = round((on / total) * 100)
      sdr['sparsity'] = sparsity
      # write to cache
      with open(cache_file, 'w') as f:
        f.write(json.dumps(sdr))

    return sdr

  def getSdr(self, term):
    return self._bitmapToSdr(self.getBitmap(term))

  def bitmapToTerms(self, onBits):
    if len(onBits) is 0:
      raise(Exception("Cannot convert empty bitmap to term!"))
    response = self.bitmapToTermsRaw(onBits)
    similar = []
    for term in response:
      similar.append(
        {'term': term['term'], 'score': term['score']}
      )
    return similar

  def bitmapToTermsRaw(self, onBits):
    url = self._buildUrl("expressions/similarTerms")
    data = json.dumps({'positions': onBits})
    cache_path = 'bitmap-' + hashlib.sha224(data).hexdigest() + '.json'
    cache_file = os.path.join(self.cache_dir, cache_path)

    # Get it from the cache if it's there.
    if os.path.exists(cache_file):
      return json.loads(open(cache_file).read())
    else:
      headers = {'Content-Type': 'application/json'}
      response = requests.post(url,
                               headers=headers,
                               data=data)

      with open(cache_file, 'w') as f:
        f.write(response.content)

      return json.loads(response.content)

  def _buildUrl(self, endpoint, params={}):
    params['api_key']    = self.api_key
    params['retinaName'] = self.retina
    return "%s%s?%s" % (self.api_url, endpoint, urllib.urlencode(params))

  def _bitmapToSdr(self, bitmap):
    width = bitmap['width']
    height = bitmap['height']
    total = width * height
    positions = bitmap['positions']
    sdr = ""
    if len(positions) is 0:
      nextOn = None
    else:
      nextOn = positions.pop(0)

    for sdrIndex in range(0, total):

      if nextOn is None or nextOn != sdrIndex:
        sdr += "0"
      else:
        sdr += "1"
        if len(positions) is 0:
          nextOn = None
        else:
          nextOn = positions.pop(0)

    return sdr
<commit_msg>Switch to CEPT endpoint for Numenta that supports tokenization. Adds tokenize method.<commit_after># The MIT License (MIT)
#
# Copyright (c) 2013 Numenta, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import hashlib
import os
import json
import requests
import urllib

DEFAULT_BASE_URL = "http://numenta.cortical.io:80/rest/"
DEFAULT_RETINA = "eng_syn"
DEFAULT_CACHE_DIR = "/tmp/pycept"
DEFAULT_VERBOSITY = 0

RETINA_SIZES = {
  "eng_syn": {
    "width": 64,
    "height": 64
  },
  "eng_syn_morph": {
    "width": 64,
    "height": 64
  },
  "eng_gen": {
    "width": 128,
    "height": 128
  }
}

class Cept(object):
  """
  Main class for the Cept API.
  """

  def __init__(self, api_key,
               base_url=DEFAULT_BASE_URL,
               retina=DEFAULT_RETINA,
               cache_dir=DEFAULT_CACHE_DIR,
               verbosity=DEFAULT_VERBOSITY):
    self.api_key = api_key
    self.api_url = base_url
    self.retina = retina
    # Create the cache directory if necessary.
    cache_dir = os.path.join(cache_dir, retina)
    if not os.path.exists(cache_dir):
      os.makedirs(cache_dir)
    self.cache_dir = cache_dir
    self.verbosity = verbosity

  def tokenize(self, text):
    """Get a list of sentence tokens from a text string.

    Example:
      >>> c = cept.Cept(apiKey)
      >>> c.tokenize('The cow jumped over the moon. Then it ran to the other '
                     'side. And then the sun came up.')
      [[u'cow', u'jumped', u'moon'], [u'ran', u'other side'], [u'sun', u'came']]

    :param text: string to tokenize
    :returns: a list of lists where each inner list contains the string tokens
        from a sentence in the input text
    """
    url = self._buildUrl("text/tokenize")
    headers = {"Content-Type": "application/json"}
    response = requests.post(url, headers=headers, data=text)
    return [sentence.split(",") for sentence in response.json()]

  def getBitmap(self, term):
    url = self._buildUrl("text")
    # Create a cache location for each term, where it will either be read in from
    # or cached within if we have to go to the CEPT API to get the SDR.
    cache_file = os.path.join(self.cache_dir, term + '.json')
    # Get it from the cache if it's there.
    if os.path.exists(cache_file):
      sdr = json.loads(open(cache_file).read())
    # Get it from CEPT API if it's not cached.
    else:
      if self.verbosity > 0:
        print '\tfetching %s from CEPT API' % term
      headers = {'Content-Type': 'application/json'}
      response = requests.post(url,
                               headers=headers,
                               data=term)
      responseObj = json.loads(response.content)
      if type(responseObj) == list:
        sdr = responseObj[0]
      else:
        sdr = {"positions": []}

      if (not 'width' in sdr) or (not 'height' in sdr):
        size = RETINA_SIZES[self.retina]
        sdr['width'] = size['width']
        sdr['height'] = size['height']

      # attach the sparsity for reference
      total = float(sdr['width']) * float(sdr['height'])
      on = len(sdr['positions'])
      sparsity = round((on / total) * 100)
      sdr['sparsity'] = sparsity
      # write to cache
      with open(cache_file, 'w') as f:
        f.write(json.dumps(sdr))

    return sdr

  def getSdr(self, term):
    return self._bitmapToSdr(self.getBitmap(term))

  def bitmapToTerms(self, onBits):
    if len(onBits) is 0:
      raise(Exception("Cannot convert empty bitmap to term!"))
    response = self.bitmapToTermsRaw(onBits)
    similar = []
    for term in response:
      similar.append(
        {'term': term['term'], 'score': term['score']}
      )
    return similar

  def bitmapToTermsRaw(self, onBits):
    url = self._buildUrl("expressions/similarTerms")
    data = json.dumps({'positions': onBits})
    cache_path = 'bitmap-' + hashlib.sha224(data).hexdigest() + '.json'
    cache_file = os.path.join(self.cache_dir, cache_path)

    # Get it from the cache if it's there.
    if os.path.exists(cache_file):
      return json.loads(open(cache_file).read())
    else:
      headers = {'Content-Type': 'application/json'}
      response = requests.post(url,
                               headers=headers,
                               data=data)

      with open(cache_file, 'w') as f:
        f.write(response.content)

      return json.loads(response.content)

  def _buildUrl(self, endpoint, params={}):
    params['api_key']    = self.api_key
    params['retinaName'] = self.retina
    return "%s%s?%s" % (self.api_url, endpoint, urllib.urlencode(params))

  def _bitmapToSdr(self, bitmap):
    width = bitmap['width']
    height = bitmap['height']
    total = width * height
    positions = bitmap['positions']
    sdr = ""
    if len(positions) is 0:
      nextOn = None
    else:
      nextOn = positions.pop(0)

    for sdrIndex in range(0, total):

      if nextOn is None or nextOn != sdrIndex:
        sdr += "0"
      else:
        sdr += "1"
        if len(positions) is 0:
          nextOn = None
        else:
          nextOn = positions.pop(0)

    return sdr
<|endoftext|>


<commit_before>"""This module deals with the metadata I have received from collaborators.

It defines the location of ring resonances for the RingCube plotting.
"""
import pandas as pd
import pkg_resources as pr

def get_meta_df():
    def read_metadata(f):
        df = pd.read_csv(f, header=None, delim_whitespace=True)
        df = df.rename(columns={0: 'id', 1: 'pixres', 14: 'lit_status'})
        df = df.set_index('id')
        df['is_lit'] = df.lit_status is True
        # df.drop('lit_status', axis=1)
        return df

    with pr.resource_stream('pyciss', 'data/metadata.txt') as f:
        meta_df = read_metadata(f)
    return meta_df

# resonances
def get_order(name):
    ratio = name.split()[1]
    a, b = ratio.split(':')
    return int(a) - int(b)

def get_resonances():
    with pr.resource_stream('pyciss', 'data/ring_resonances.csv') as f:
        resonances = pd.read_csv(f)
    resonances.columns = ['name', 'radius', 'a_moon', 'n', 'kappa']
    resonances = resonances.sort_values(by='radius', ascending=True)
    resonances['order'] = resonances.name.map(get_order)
    return resonances

def get_prime_resonances():
    resonances = get_resonances()
    prime_resonances = resonances[resonances.order == 1].drop('order', axis=1)
    # filter out Janus and Epimetheus as we have a more precise file for that.
    prime_resonances = prime_resonances.loc[~prime_resonances.name.str.startswith('Janus')]
    prime_resonances = prime_resonances.loc[~prime_resonances.name.str.startswith('Epimetheus')]
    return prime_resonances

# Janus Epithemeus resonances
def get_janus_epimetheus_resonances():
    w = [len('              Janus1'),
         len(' reson'),
         len('  Resonance radius R')]

    def get_janos_epi_order(reso):
        a, b = reso.split(':')
        return int(a) - int(b)

    fname = pr.resource_filename('pyciss',
                                 'data/ring_janus_epimetheus_resonances.txt')
    with open(fname) as f:
        jan_epi_resonances = pd.read_fwf(f, skiprows=15, header=0, widths=w,
                                         skipfooter=1)

    # replace column names
    jan_epi_resonances.columns = ['moon', 'reson', 'radius']

    # calculate order from resonance name
    jan_epi_resonances['order'] = jan_epi_resonances.reson.map(get_janos_epi_order)

    def func(x):
        "Remove space from resonce string"
        return ':'.join(i.strip() for i in x.split(':'))
    jan_epi_resonances.reson = jan_epi_resonances.reson.map(func)

    # calculate name for axes display
    jan_epi_resonances['name'] = jan_epi_resonances.moon + ' ' +\
        jan_epi_resonances.reson
    return jan_epi_resonances

def get_prime_jan_epi():
    jan_epi_resonances = get_janus_epimetheus_resonances()
    # remove orders > 1 and drop unrequired columns
    prime_jan_epis = jan_epi_resonances[jan_epi_resonances.order == 1]
    to_drop = ['order', 'moon', 'reson']
    prime_jan_epis = prime_jan_epis.drop(to_drop, axis=1)
    return prime_jan_epis

def get_all_resonances():
    prime_resonances = get_prime_resonances()
    prime_jan_epis = get_prime_jan_epi()
    all_resonances = pd.concat([prime_resonances, prime_jan_epis])
    all_resonances.sort_values(by='radius', inplace=True)
    all_resonances['moon'] = all_resonances.name.map(lambda x: x.split()[0].lower())
    return all_resonances
<commit_msg>remove dropping the reson column<commit_after>"""This module deals with the metadata I have received from collaborators.

It defines the location of ring resonances for the RingCube plotting.
"""
import pandas as pd
import pkg_resources as pr

def get_meta_df():
    def read_metadata(f):
        df = pd.read_csv(f, header=None, delim_whitespace=True)
        df = df.rename(columns={0: 'id', 1: 'pixres', 14: 'lit_status'})
        df = df.set_index('id')
        df['is_lit'] = df.lit_status is True
        # df.drop('lit_status', axis=1)
        return df

    with pr.resource_stream('pyciss', 'data/metadata.txt') as f:
        meta_df = read_metadata(f)
    return meta_df

# resonances
def get_order(name):
    ratio = name.split()[1]
    a, b = ratio.split(':')
    return int(a) - int(b)

def get_resonances():
    with pr.resource_stream('pyciss', 'data/ring_resonances.csv') as f:
        resonances = pd.read_csv(f)
    resonances.columns = ['name', 'radius', 'a_moon', 'n', 'kappa']
    resonances = resonances.sort_values(by='radius', ascending=True)
    resonances['order'] = resonances.name.map(get_order)
    return resonances

def get_prime_resonances():
    resonances = get_resonances()
    prime_resonances = resonances[resonances.order == 1].drop('order', axis=1)
    # filter out Janus and Epimetheus as we have a more precise file for that.
    prime_resonances = prime_resonances.loc[~prime_resonances.name.str.startswith('Janus')]
    prime_resonances = prime_resonances.loc[~prime_resonances.name.str.startswith('Epimetheus')]
    return prime_resonances

# Janus Epithemeus resonances
def get_janus_epimetheus_resonances():
    w = [len('              Janus1'),
         len(' reson'),
         len('  Resonance radius R')]

    def get_janos_epi_order(reso):
        a, b = reso.split(':')
        return int(a) - int(b)

    fname = pr.resource_filename('pyciss',
                                 'data/ring_janus_epimetheus_resonances.txt')
    with open(fname) as f:
        jan_epi_resonances = pd.read_fwf(f, skiprows=15, header=0, widths=w,
                                         skipfooter=1)

    # replace column names
    jan_epi_resonances.columns = ['moon', 'reson', 'radius']

    # calculate order from resonance name
    jan_epi_resonances['order'] = jan_epi_resonances.reson.map(get_janos_epi_order)

    def func(x):
        "Remove space from resonce string"
        return ':'.join(i.strip() for i in x.split(':'))
    jan_epi_resonances.reson = jan_epi_resonances.reson.map(func)

    # calculate name for axes display
    jan_epi_resonances['name'] = jan_epi_resonances.moon + ' ' +\
        jan_epi_resonances.reson
    return jan_epi_resonances

def get_prime_jan_epi():
    jan_epi_resonances = get_janus_epimetheus_resonances()
    # remove orders > 1 and drop unrequired columns
    prime_jan_epis = jan_epi_resonances[jan_epi_resonances.order == 1]
    to_drop = ['order', 'moon']
    prime_jan_epis = prime_jan_epis.drop(to_drop, axis=1)
    return prime_jan_epis

def get_all_resonances():
    prime_resonances = get_prime_resonances()
    prime_jan_epis = get_prime_jan_epi()
    all_resonances = pd.concat([prime_resonances, prime_jan_epis])
    all_resonances.sort_values(by='radius', inplace=True)
    all_resonances['moon'] = all_resonances.name.map(lambda x: x.split()[0].lower())
    return all_resonances
<|endoftext|>


<commit_before>from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from django.conf import settings

from .forms import ODMForm
from .models import Build

from subprocess import call, Popen, PIPE
from os.path import dirname, join, exists

# Use 12factor inspired environment variables or from a file
import environ

@login_required
def index(request):
	if request.method == 'POST':
		form = ODMForm(request.POST, request.FILES)
		if form.is_valid():
			build = form.save(commit=False)
			build.user = request.user
			build.save()

			# Main Logic goes here
			source_file_name = build.source_file.name
			build_file_name = build.buildprop_file.name

			env = environ.Env()
			env_file = join(dirname(__file__), 'config.env')
			if exists(env_file):
			    environ.Env.read_env(str(env_file))

			MMX_KEY_PATH = env('MMX_KEY_PATH')
			PATH_OF_SOURCE_FILE = env('PATH_OF_SOURCE_FILE')
			#NEW_PATH_OF_SIGNED_TARGET_FILES = env('NEW_PATH_OF_SIGNED_TARGET_FILES')
			NEW_PATH_OF_SIGNED_TARGET_FILES = '/mnt/ODM/' + build.odm.lower()

			MEDIA_PATH = settings.MEDIA_ROOT
			build_file = join(MEDIA_PATH, build_file_name)
			#sign_file = join(MEDIA_PATH, 'Sign.sh')

			sign_file = '/mnt/yuos/Sign.sh'

			print(build_file)

			import re
			build_str = open(build_file, "r")
			for line in build_str:
				if re.match("ro.build.id", line):
					build_id = line.split('=')[1]
				if re.match("ro.build.fingerprint", line):
					build_version = line.split('=')[1].split('/')[4].split(':')[0]
				if re.match("ro.yu.device", line):
					target_product = line.split('=')[1].strip()
					NEW_PATH_OF_SIGNED_TARGET_FILES += '/' + target_product + '/'

			# If target directory does't exists then create one
			import os
			if not os.path.exists(NEW_PATH_OF_SIGNED_TARGET_FILES):
				os.makedirs(NEW_PATH_OF_SIGNED_TARGET_FILES)

			# Move uploaded source file to /mnt/ODM/(odm name)/(device name)
			import shutil
			shutil.move(os.getcwd() + '/media/' + source_file_name, NEW_PATH_OF_SIGNED_TARGET_FILES)

			call('export target_file_name="' + source_file_name + '"', shell=True)
			call('export mmx_key_path="' + MMX_KEY_PATH + '"', shell=True)
			call('export path_of_target_file="' + NEW_PATH_OF_SIGNED_TARGET_FILES + '"', shell=True)
			call('export new_path_of_signed_target_files="' + NEW_PATH_OF_SIGNED_TARGET_FILES + '"', shell=True)
			call('export OS_Android_version="' + build.android_version + '"', shell=True)
			call('export Build_ID="' + build_id + '"', shell=True)
			call('export mmx_build_version="' + build_version + '"', shell=True)
			call('export target_product="' + target_product + '"', shell=True)
			os.chdir("/mnt/yuos")
			call('. ' + sign_file + ' ' + build.build_type, shell=True)

			return redirect('/builder/home/')
	else:
		form = ODMForm()

	return render(request, 'odm_builder/index.html', {'form': form})
<commit_msg>Fixed a problem of recursive directory creation for a product if 'ro.yu.device' is present twice in build.prop<commit_after>from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from django.conf import settings

from .forms import ODMForm
from .models import Build

from subprocess import call, Popen, PIPE
from os.path import dirname, join, exists

# Use 12factor inspired environment variables or from a file
import environ

@login_required
def index(request):
	if request.method == 'POST':
		form = ODMForm(request.POST, request.FILES)
		if form.is_valid():
			build = form.save(commit=False)
			build.user = request.user
			build.save()

			# Main Logic goes here
			source_file_name = build.source_file.name
			build_file_name = build.buildprop_file.name

			env = environ.Env()
			env_file = join(dirname(__file__), 'config.env')
			if exists(env_file):
			    environ.Env.read_env(str(env_file))

			MMX_KEY_PATH = env('MMX_KEY_PATH')
			PATH_OF_SOURCE_FILE = env('PATH_OF_SOURCE_FILE')
			#NEW_PATH_OF_SIGNED_TARGET_FILES = env('NEW_PATH_OF_SIGNED_TARGET_FILES')
			NEW_PATH_OF_SIGNED_TARGET_FILES = '/mnt/ODM/' + build.odm.lower()

			MEDIA_PATH = settings.MEDIA_ROOT
			build_file = join(MEDIA_PATH, build_file_name)
			#sign_file = join(MEDIA_PATH, 'Sign.sh')

			sign_file = '/mnt/yuos/Sign.sh'

			import re
			build_str = open(build_file, "r")
			target_product_found = False
			for line in build_str:
				if re.match("ro.build.id", line):
					build_id = line.split('=')[1]
				if re.match("ro.build.fingerprint", line):
					build_version = line.split('=')[1].split('/')[4].split(':')[0]
				if re.match("ro.yu.device", line) and not target_product_found:
					target_product = line.split('=')[1].strip()
					target_product_found = True
					NEW_PATH_OF_SIGNED_TARGET_FILES += '/' + target_product + '/'

			# If target directory does't exists then create one
			import os
			if not os.path.exists(NEW_PATH_OF_SIGNED_TARGET_FILES):
				os.makedirs(NEW_PATH_OF_SIGNED_TARGET_FILES)

			# Move uploaded source file to /mnt/ODM/(odm name)/(device name)
			import shutil
			shutil.move(os.getcwd() + '/media/' + source_file_name, NEW_PATH_OF_SIGNED_TARGET_FILES)

			call('export target_file_name="' + source_file_name + '"', shell=True)
			call('export mmx_key_path="' + MMX_KEY_PATH + '"', shell=True)
			call('export path_of_target_file="' + NEW_PATH_OF_SIGNED_TARGET_FILES + '"', shell=True)
			call('export new_path_of_signed_target_files="' + NEW_PATH_OF_SIGNED_TARGET_FILES + '"', shell=True)
			call('export OS_Android_version="' + build.android_version + '"', shell=True)
			call('export Build_ID="' + build_id + '"', shell=True)
			call('export mmx_build_version="' + build_version + '"', shell=True)
			call('export target_product="' + target_product + '"', shell=True)
			os.chdir("/mnt/yuos")
			call('. ' + sign_file + ' ' + build.build_type, shell=True)

			return redirect('/builder/home/')
	else:
		form = ODMForm()

	return render(request, 'odm_builder/index.html', {'form': form})
<|endoftext|>


<commit_before>#!/usr/bin/env python3
import re
import traceback
import json
import logging

from . import utils
from .data import STATUSES
from . import errors

class Handler(object):

    request = None
    response = None

    def options(self):
        """Default implementation of OPTIONS method.

        Checks which methods are implemented, and sets the Allow header
        accordingly.
        """
        # Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
        http_methods = [
            'get',
            'head',
            'post',
            'put',
            'delete',
            'connect',
            'options',
            'trace',
            'patch'
        ]

        supported_methods = []

        for method in http_methods:
            if method in dir(self):
                supported_methods.append(method.upper())

        self.response.set_header('Allow', ', '.join(supported_methods))
        return {}

class Request(object):

    def __init__(self, path, method, environ):
        self._headers = None
        self._args = None
        self._json_args = None
        self._cookies = None
        self._body = None
        self._data = None

        self.path = path
        self.method = method.upper()
        self.environ = environ

    def get_header(self, name, default=None):
        return self.headers.get(name.title(), default)

    @property
    def headers(self):
        if self._headers is not None:
            return self._headers
        self._headers = {}
        for key, value in self.environ.items():
            if key == "CONTENT_TYPE" or key == "CONTENT_LENGTH":
                header = key.title().replace("_", "-")
                self._headers[header] = value
            elif key.startswith("HTTP_"):
                header = key[5:].title().replace("_", "-")
                self._headers[header] = value
        return self._headers

    @property
    def args(self):
        if self._args is not None:
            return self._args
        qs = self.environ["QUERY_STRING"]
        self._args = utils.query_string_to_dict(qs)
        return self._args

    @property
    def json_args(self):
        if self._json_args is not None:
            return self._json_args

        try:
            qs = self.environ["QUERY_STRING"]
            self._json_args = utils.query_string_to_json(qs)
        except Exception:
            raise errors.HTTP_400("Invalid JSON in request query string")

        return self._json_args

    @property
    def body(self):
        if self._body is not None:
            return self._body
        try:
            length = int(self.environ.get("CONTENT_LENGTH", "0"))
        except ValueError:
            length = 0
        if length > 0:
            self._body = self.environ['wsgi.input'].read(length)
        else:
            self._body = ''

        return self._body

    @property
    def cookies(self):
        if self._cookies is not None:
            return self._cookies

        self._cookies = {}

        if 'HTTP_COOKIE' in self.environ:
            for cookie_line in self.environ['HTTP_COOKIE'].split(';'):
                if "DELETED" in cookie_line:
                    continue
                cname, cvalue = cookie_line.strip().split('=', 1)
                self._cookies[cname] = cvalue

        return self._cookies

    @property
    def data(self):
        if self._data is not None:
            return self._data
        if self.body:
            try:
                self._data = json.loads(self.body.decode('utf-8'))
            except Exception:
                raise errors.HTTP_400("Expected JSON in request body")
        else:
            self._data = {}

        return self._data

    @property
    def ip(self):
        try:
            return self.environ['HTTP_X_FORWARDED_FOR'].split(',')[-1].strip()
        except KeyError:
            return self.environ['REMOTE_ADDR']

class Response(object):

    def __init__(self, status_code):
        self.header_dict = {
            "Content-Type": "application/json"
        }
        self._headers = []
        self.cookie_dict = {}
        self.status_code = status_code

    def set_header(self, key, value):
        self.header_dict[key] = value

    def set_cookie(
            self, key, value, expires="", path='/', domain="", flags=[]):
        value = value.replace(";", "")  # ; not allowed
        if expires:
            expires = "expires=%s; " % (expires)
        if domain:
            domain = "Domain=%s; " % (domain)

        self.cookie_dict[key] = "%s;%s%s path=%s; %s"\
            % (value, domain, expires, path, "; ".join(flags))

    def delete_cookie(self, key):
        expiry_string = 'Thu, 01 Jan 1970 00:00:00 GMT'
        self.set_cookie(key, "DELETED", expires=expiry_string)

    @property
    def headers(self):
        self._headers = []
        for k, v in self.header_dict.items():
            self._headers.append((k, v))
        for k, v in self.cookie_dict.items():
            self._headers.append(('Set-Cookie', '%s=%s' % (k, v)))
        return self._headers

    @property
    def status(self):
        if self.status_code in STATUSES:
            return STATUSES[self.status_code]
        print("Warning! Status %s does not exist!" % (self.status_code))
        return STATUSES[577]

class WSGI:

    routes = []
    debug = False
    logger = None
    before = None
    teardown = None
    after = None
    headers = None
    strip_path = True
    json_cls = None

    def __init__(self, environ, start_response):

        if not self.logger:
            self.logger = logging.Logger(__name__)

        self.logger.debug("WSGI __init__ called")

        path = environ["PATH_INFO"]
        if self.strip_path and len(path) > 1 and path.endswith("/"):
            path = path[0:-1]

        self.request = Request(
            path=path,
            method=environ["REQUEST_METHOD"],
            environ=environ
        )

        self.response = Response(status_code=200)

        self.environ = environ
        self.start = start_response
        self.response.set_header("Content-Type", "application/json")

    def __iter__(self):
        try:
            if self.before:
                self.before()
            resp = self.delegate()
            if self.after:
                self.after()
            headers = self.response.headers
            if self.headers:
                headers += self.headers
            self.start(self.response.status, headers)

        except errors.HTTPError as err:
            self.response.status_code = err.status_code
            headers = [("Content-Type", "application/json")]
            if self.headers:
                headers += self.headers
            if err.headers:
                headers += err.headers
            self.start(self.response.status, headers)
            resp = err.response()

        except Exception as err:
            self.logger.exception(err)
            headers = [("Content-Type", "application/json")]
            self.start(STATUSES[500], headers)
            if self.debug:
                resp = {"error": traceback.format_exc().split("\n")}
            else:
                resp = {"error": "Internal server error encountered."}

        if self.teardown:
            self.teardown()

        if isinstance(resp, (dict, list)):
            try:
                if self.debug:
                    jresp = json.dumps(resp, indent=4, cls=self.json_cls)
                else:
                    jresp = json.dumps(resp, cls=self.json_cls)
            except Exception:
                if self.debug:
                    msg = traceback.format_exc().split("\n")
                    jresp = json.dumps({"error": msg}, indent=4)
                else:
                    msg = "An unhandled exception occured during response"
                    jresp = json.dumps({"error": msg})
            self.logger.debug("Sending JSON response: %s", jresp)
            return iter([jresp.encode('utf-8')])
        elif isinstance(resp, str):
            self.logger.debug("Sending string response: %s", resp)
            return iter([resp.encode('utf-8')])
        else:
            self.logger.debug("Sending unknown response: %s", resp)
            return iter(resp)

    def delegate(self):
        path = self.request.path
        method = self.request.method

        for pattern, handler in self.routes:
            # Set defaults for handler
            handler.request = self.request
            handler.response = self.response

            m = re.match('^' + pattern + '$', path)
            if m:
                if hasattr(handler, 'before'):
                    handler.before()

                args = m.groups()
                funcname = method.lower()
                try:
                    func = getattr(handler, funcname)
                except AttributeError:
                    raise errors.HTTP_405("%s not allowed" % (method.upper()))

                output = func(*args)

                if hasattr(handler, 'after'):
                    handler.after()

                return output

        raise errors.HTTP_404("Path %s not found" % (path))
<commit_msg>fix for #31 - malformed cookie<commit_after>#!/usr/bin/env python3
import re
import traceback
import json
import logging

from . import utils
from .data import STATUSES
from . import errors

class Handler(object):

    request = None
    response = None

    def options(self):
        """Default implementation of OPTIONS method.

        Checks which methods are implemented, and sets the Allow header
        accordingly.
        """
        # Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
        http_methods = [
            'get',
            'head',
            'post',
            'put',
            'delete',
            'connect',
            'options',
            'trace',
            'patch'
        ]

        supported_methods = []

        for method in http_methods:
            if method in dir(self):
                supported_methods.append(method.upper())

        self.response.set_header('Allow', ', '.join(supported_methods))
        return {}

class Request(object):

    def __init__(self, path, method, environ):
        self._headers = None
        self._args = None
        self._json_args = None
        self._cookies = None
        self._body = None
        self._data = None

        self.path = path
        self.method = method.upper()
        self.environ = environ

    def get_header(self, name, default=None):
        return self.headers.get(name.title(), default)

    @property
    def headers(self):
        if self._headers is not None:
            return self._headers
        self._headers = {}
        for key, value in self.environ.items():
            if key == "CONTENT_TYPE" or key == "CONTENT_LENGTH":
                header = key.title().replace("_", "-")
                self._headers[header] = value
            elif key.startswith("HTTP_"):
                header = key[5:].title().replace("_", "-")
                self._headers[header] = value
        return self._headers

    @property
    def args(self):
        if self._args is not None:
            return self._args
        qs = self.environ["QUERY_STRING"]
        self._args = utils.query_string_to_dict(qs)
        return self._args

    @property
    def json_args(self):
        if self._json_args is not None:
            return self._json_args

        try:
            qs = self.environ["QUERY_STRING"]
            self._json_args = utils.query_string_to_json(qs)
        except Exception:
            raise errors.HTTP_400("Invalid JSON in request query string")

        return self._json_args

    @property
    def body(self):
        if self._body is not None:
            return self._body
        try:
            length = int(self.environ.get("CONTENT_LENGTH", "0"))
        except ValueError:
            length = 0
        if length > 0:
            self._body = self.environ['wsgi.input'].read(length)
        else:
            self._body = ''

        return self._body

    @property
    def cookies(self):
        if self._cookies is not None:
            return self._cookies

        self._cookies = {}

        if 'HTTP_COOKIE' in self.environ:
            for cookie_line in self.environ['HTTP_COOKIE'].split(';'):
                if "=" not in cookie_line:
                    continue  # malformed cookie
                if "DELETED" in cookie_line:
                    continue
                cname, cvalue = cookie_line.strip().split('=', 1)
                self._cookies[cname] = cvalue

        return self._cookies

    @property
    def data(self):
        if self._data is not None:
            return self._data
        if self.body:
            try:
                self._data = json.loads(self.body.decode('utf-8'))
            except Exception:
                raise errors.HTTP_400("Expected JSON in request body")
        else:
            self._data = {}

        return self._data

    @property
    def ip(self):
        try:
            return self.environ['HTTP_X_FORWARDED_FOR'].split(',')[-1].strip()
        except KeyError:
            return self.environ['REMOTE_ADDR']

class Response(object):

    def __init__(self, status_code):
        self.header_dict = {
            "Content-Type": "application/json"
        }
        self._headers = []
        self.cookie_dict = {}
        self.status_code = status_code

    def set_header(self, key, value):
        self.header_dict[key] = value

    def set_cookie(
            self, key, value, expires="", path='/', domain="", flags=[]):
        value = value.replace(";", "")  # ; not allowed
        if expires:
            expires = "expires=%s; " % (expires)
        if domain:
            domain = "Domain=%s; " % (domain)

        self.cookie_dict[key] = "%s;%s%s path=%s; %s"\
            % (value, domain, expires, path, "; ".join(flags))

    def delete_cookie(self, key):
        expiry_string = 'Thu, 01 Jan 1970 00:00:00 GMT'
        self.set_cookie(key, "DELETED", expires=expiry_string)

    @property
    def headers(self):
        self._headers = []
        for k, v in self.header_dict.items():
            self._headers.append((k, v))
        for k, v in self.cookie_dict.items():
            self._headers.append(('Set-Cookie', '%s=%s' % (k, v)))
        return self._headers

    @property
    def status(self):
        if self.status_code in STATUSES:
            return STATUSES[self.status_code]
        print("Warning! Status %s does not exist!" % (self.status_code))
        return STATUSES[577]

class WSGI:

    routes = []
    debug = False
    logger = None
    before = None
    teardown = None
    after = None
    headers = None
    strip_path = True
    json_cls = None

    def __init__(self, environ, start_response):

        if not self.logger:
            self.logger = logging.Logger(__name__)

        self.logger.debug("WSGI __init__ called")

        path = environ["PATH_INFO"]
        if self.strip_path and len(path) > 1 and path.endswith("/"):
            path = path[0:-1]

        self.request = Request(
            path=path,
            method=environ["REQUEST_METHOD"],
            environ=environ
        )

        self.response = Response(status_code=200)

        self.environ = environ
        self.start = start_response
        self.response.set_header("Content-Type", "application/json")

    def __iter__(self):
        try:
            if self.before:
                self.before()
            resp = self.delegate()
            if self.after:
                self.after()
            headers = self.response.headers
            if self.headers:
                headers += self.headers
            self.start(self.response.status, headers)

        except errors.HTTPError as err:
            self.response.status_code = err.status_code
            headers = [("Content-Type", "application/json")]
            if self.headers:
                headers += self.headers
            if err.headers:
                headers += err.headers
            self.start(self.response.status, headers)
            resp = err.response()

        except Exception as err:
            self.logger.exception(err)
            headers = [("Content-Type", "application/json")]
            self.start(STATUSES[500], headers)
            if self.debug:
                resp = {"error": traceback.format_exc().split("\n")}
            else:
                resp = {"error": "Internal server error encountered."}

        if self.teardown:
            self.teardown()

        if isinstance(resp, (dict, list)):
            try:
                if self.debug:
                    jresp = json.dumps(resp, indent=4, cls=self.json_cls)
                else:
                    jresp = json.dumps(resp, cls=self.json_cls)
            except Exception:
                if self.debug:
                    msg = traceback.format_exc().split("\n")
                    jresp = json.dumps({"error": msg}, indent=4)
                else:
                    msg = "An unhandled exception occured during response"
                    jresp = json.dumps({"error": msg})
            self.logger.debug("Sending JSON response: %s", jresp)
            return iter([jresp.encode('utf-8')])
        elif isinstance(resp, str):
            self.logger.debug("Sending string response: %s", resp)
            return iter([resp.encode('utf-8')])
        else:
            self.logger.debug("Sending unknown response: %s", resp)
            return iter(resp)

    def delegate(self):
        path = self.request.path
        method = self.request.method

        for pattern, handler in self.routes:
            # Set defaults for handler
            handler.request = self.request
            handler.response = self.response

            m = re.match('^' + pattern + '$', path)
            if m:
                if hasattr(handler, 'before'):
                    handler.before()

                args = m.groups()
                funcname = method.lower()
                try:
                    func = getattr(handler, funcname)
                except AttributeError:
                    raise errors.HTTP_405("%s not allowed" % (method.upper()))

                output = func(*args)

                if hasattr(handler, 'after'):
                    handler.after()

                return output

        raise errors.HTTP_404("Path %s not found" % (path))
<|endoftext|>


<commit_before>from __future__ import absolute_import

"""
BaseMap is a generic Map class from which all other Map classes inherit from.
"""

#pylint: disable=E1101,E1121
__authors__ = ["Keith Hughitt, Steven Christe"]
__email__ = "keith.hughitt@nasa.gov"

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.colors as colors
import matplotlib.cm as cm
from datetime import datetime
from sunpy.solwcs import solwcs as wcs

"""
Questions
---------
1. map.wavelength, map.meas or? (use hv/vso/etc conventions?)
2. Are self.r_sun and radius below different? (rsun or rsun_obs for AIA?)
"""

class BaseMap(np.ndarray):
    """
    BaseMap(data, header)

    A spatially-aware data array based on the SolarSoft Map object

    Parameters
    ----------
    data : numpy.ndarray, list
        A 2d list or ndarray containing the map data
    header : dict
        A dictionary of the original image header tags

    Attributes
    ----------
    header : dict
        A dictionary representation of the image header
    date : datetime
        Image observation time
    det : str
        Detector name
    inst : str
        Instrument name
    meas : str, int
        Measurement name. For AIA this is the wavelength of image
    obs : str
        Observatory name
    rsun : float
        Radius of the sun
    name : str
        Nickname for the image type (e.g. "AIA 171")
    center : dict
        X and Y coordinate for the center of the sun in units
    scale: dict
        Image scale along the x and y axes in units/pixel
    units: dict
        Image coordinate units along the x and y axes

    Examples
    --------
    >>> aia = sunpy.Map(sunpy.AIA_171_IMAGE)
    >>> aia.T
    AIAMap([[ 0.3125,  1.    , -1.1875, ..., -0.625 ,  0.5625,  0.5   ],
    [-0.0625,  0.1875,  0.375 , ...,  0.0625,  0.0625, -0.125 ],
    [-0.125 , -0.8125, -0.5   , ..., -0.3125,  0.5625,  0.4375],
    ...,
    [ 0.625 ,  0.625 , -0.125 , ...,  0.125 , -0.0625,  0.6875],
    [-0.625 , -0.625 , -0.625 , ...,  0.125 , -0.0625,  0.6875],
    [ 0.    ,  0.    , -1.1875, ...,  0.125 ,  0.    ,  0.6875]])
    >>> aia.header.get('cunit1')
    'arcsec'
    >>> aia.show()
    >>> import matplotlib.cm as cm
    >>> import matplotlib.colors as colors
    >>> aia.show(cmap=cm.hot, norm=colors.Normalize(1, 2048))

    See Also
    --------
    numpy.ndarray Parent class for the Map object

    References
    ----------
    | http://docs.scipy.org/doc/numpy/reference/arrays.classes.html
    | http://docs.scipy.org/doc/numpy/user/basics.subclassing.html
    | http://docs.scipy.org/doc/numpy/reference/ufuncs.html
    | http://www.scipy.org/Subclasses

    """
    def __new__(cls, data, header=None): #pylint: disable=W0613
        """Creates a new BaseMap instance"""
        if isinstance(data, np.ndarray):
            obj = data.view(cls)
        elif isinstance(data, list):
            obj = np.asarray(data).view(cls)

        return obj

    def __init__(self, data, header=None): #pylint: disable=W0613
        """BaseMap constructor"""
        if header:
            self.header = header
            self.date = None
            self.name = None
            self.cmap = None
            self.norm = None

            # Set object attributes dynamically
            for attr, value in list(self.get_properties(header).items()):
                setattr(self, attr, value)

            self.center = {
                "x": wcs.get_center(header, axis='x'),
                "y": wcs.get_center(header, axis='y')
            }
            self.scale = {
                "x": header.get('cdelt1'),
                "y": header.get('cdelt2')
            }
            self.units = {
                "x": wcs.get_units(header, axis='x'),
                "y": wcs.get_units(header, axis='y')
            }
            self.rsun = wcs.solar_limb(header)

    def __array_finalize__(self, obj):
        """Finishes instantiation of the new map object"""
        if obj is None:
            return

        if hasattr(obj, 'header'):
            self.header = obj.header

            # preserve object properties
            properties = self.get_properties(obj.header)
            for attr, value in list(properties.items()):
                setattr(self, attr, getattr(obj, attr, value))

            self.center = obj.center
            self.scale = obj.scale
            self.units = obj.units

    def __array_wrap__(self, out_arr, context=None):
        """Returns a wrapped instance of a Map object"""
        return np.ndarray.__array_wrap__(self, out_arr, context)

    def __add__(self, other):
        """Add two maps. Currently does not take into account the alignment
        between the two maps."""
        result = np.ndarray.__add__(self, other)

        return result

    def __getitem__(self, key):
        """Overiding indexing operation to ensure that header is updated"""
        if isinstance(key, tuple) and type(key[0]) is slice:
            x_range = [key[1].start, key[1].stop]
            y_range = [key[0].start, key[0].stop]

            return self.submap(y_range, x_range, units="pixels")
        else:
            return np.ndarray.__getitem__(self, key)

    def __sub__(self, other):
        """Subtract two maps. Currently does not take into account the alignment
        between the two maps."""
        result = np.ndarray.__sub__(self, other)

        minmax = np.array([abs(result.min()), abs(result.max())]).max()
        result.norm = colors.Normalize(-minmax, minmax, True)

        result.cmap = cm.gray #@UndefinedVariable

        return result

    @classmethod
    def get_properties(cls):
        """Returns default map properties"""
        return {
            'cmap': cm.gray,  #@UndefinedVariable
            'norm': colors.Normalize(5, 1024, True),
            'date': datetime.today(),
            'det': "None",
            'inst': "None",
            'meas': "None",
            'obs': "None",
            'name': "Default Map",
            'r_sun': None
        }

    def get_xrange(self):
        """Return the X range of the image in arcsec."""
        xmin = self.center['x'] - self.shape[1] / 2 * self.scale['x']
        xmax = self.center['x'] + self.shape[1] / 2 * self.scale['x']
        return [xmin, xmax]

    def get_yrange(self):
        """Return the Y range of the image in arcsec."""
        ymin = self.center['y'] - self.shape[0] / 2 * self.scale['y']
        ymax = self.center['y'] + self.shape[0] / 2 * self.scale['y']
        return [ymin, ymax]

    def submap(self, range_a, range_b, units="arcseconds"):
        """Returns a submap of the map with the specified range

        Parameters
        ----------
        range_a : list
            The range of data to select across either the x axis (if
            units='arcseconds') or the y axis (if units='pixels').
        range_b : list
            The range of data to select across either the y axis (if
            units='arcseconds') or the x axis (if units='pixels').
        units : {'arcseconds' | 'pixels'}, optional
            The units for which the submap region has been specified.

        Returns
        -------
        out : BaseMap
            A new map instance is returned representing to specified sub-region.

        Examples
        --------
        >>> aia.submap([-5,5],[-5,5])
        AIAMap([[ 341.3125,  266.5   ,  329.375 ,  330.5625,  298.875 ],
        [ 347.1875,  273.4375,  247.4375,  303.5   ,  305.3125],
        [ 322.8125,  302.3125,  298.125 ,  299.    ,  261.5   ],
        [ 334.875 ,  289.75  ,  269.25  ,  256.375 ,  242.3125],
        [ 273.125 ,  241.75  ,  248.8125,  263.0625,  249.0625]])

        >>> aia.submap([0,5],[0,5], units='pixels')
        AIAMap([[ 0.3125, -0.0625, -0.125 ,  0.    , -0.375 ],
        [ 1.    ,  0.1875, -0.8125,  0.125 ,  0.3125],
        [-1.1875,  0.375 , -0.5   ,  0.25  , -0.4375],
        [-0.6875, -0.3125,  0.8125,  0.0625,  0.1875],
        [-0.875 ,  0.25  ,  0.1875,  0.    , -0.6875]])
        """
        # Arcseconds => Pixels
        #  x_px = (x / cdelt1) + (width / 2)
        #
        if units is "arcseconds":
            height = self.shape[0]
            width = self.shape[1]

            x_pixels = (np.array(range_a) / self.scale['x']) + (width / 2)
            y_pixels = (np.array(range_b) / self.scale['y']) + (height / 2)

        elif units is "pixels":
            x_pixels = range_b
            y_pixels = range_a

        # Make a copy of the header with updated centering information
        header = self.header.copy()
        header['crpix1'] = header['crpix1'] - x_pixels[0]
        header['crpix2'] = header['crpix2'] - y_pixels[0]
        header['naxis1'] = x_pixels[1] - x_pixels[0]
        header['naxis2'] = y_pixels[1] - y_pixels[0]

        # Get ndarray representation of submap
        data = np.asarray(self)[y_pixels[0]:y_pixels[1],
                                x_pixels[0]:x_pixels[1]]

        return self.__class__(data, header)

    def plot(self, draw_limb=False, **matplot_args):
        """Plots the map object using matplotlib

        Parameters
        ----------
        draw_limb : bool
            Whether a circle should be drawn around the solar limb.
        **matplot_args : dict
            Matplotlib Any additional imshow arguments that should be used
            when plotting the image.
        """
        # Create a figure and add title and axes
        fig = plt.figure()

        axes = fig.add_subplot(111)
        axes.set_title("%s %s" % (self.name, self.date))
        axes.set_xlabel('X-postion [' + self.units['x'] + ']')
        axes.set_ylabel('Y-postion [' + self.units['y'] + ']')

        # Draw circle at solar limb
        if draw_limb:
            circ = patches.Circle([0, 0], radius=self.rsun,
                fill=False, color='white')
            axes.add_artist(circ)

        # Determine extent
        extent = self.get_xrange() + self.get_yrange()

        # Matplotlib arguments
        params = {
            "cmap": self.cmap,
            "norm": self.norm
        }
        params.update(matplot_args)

        im = axes.imshow(self, origin='lower', extent=extent, **params)
        fig.colorbar(im)
        return fig

    def show(self, draw_limb=False, **matplot_args):
        self.plot(draw_limb, **matplot_args).show()

class UnrecognizedDataSouceError(ValueError):
    """Exception to raise when an unknown datasource is encountered"""
    pass
<commit_msg>Add overlays.<commit_after>from __future__ import absolute_import

"""
BaseMap is a generic Map class from which all other Map classes inherit from.
"""

#pylint: disable=E1101,E1121
__authors__ = ["Keith Hughitt, Steven Christe"]
__email__ = "keith.hughitt@nasa.gov"

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.colors as colors
import matplotlib.cm as cm
from datetime import datetime
from sunpy.solwcs import solwcs as wcs

"""
Questions
---------
1. map.wavelength, map.meas or? (use hv/vso/etc conventions?)
2. Are self.r_sun and radius below different? (rsun or rsun_obs for AIA?)
"""

def draw_limb(map_, fig, axes):
    circ = patches.Circle([0, 0], radius=map_.rsun,
        fill=False, color='white')
    axes.add_artist(circ)
    return fig, axes
_draw_limb = draw_limb

class BaseMap(np.ndarray):
    """
    BaseMap(data, header)

    A spatially-aware data array based on the SolarSoft Map object

    Parameters
    ----------
    data : numpy.ndarray, list
        A 2d list or ndarray containing the map data
    header : dict
        A dictionary of the original image header tags

    Attributes
    ----------
    header : dict
        A dictionary representation of the image header
    date : datetime
        Image observation time
    det : str
        Detector name
    inst : str
        Instrument name
    meas : str, int
        Measurement name. For AIA this is the wavelength of image
    obs : str
        Observatory name
    rsun : float
        Radius of the sun
    name : str
        Nickname for the image type (e.g. "AIA 171")
    center : dict
        X and Y coordinate for the center of the sun in units
    scale: dict
        Image scale along the x and y axes in units/pixel
    units: dict
        Image coordinate units along the x and y axes

    Examples
    --------
    >>> aia = sunpy.Map(sunpy.AIA_171_IMAGE)
    >>> aia.T
    AIAMap([[ 0.3125,  1.    , -1.1875, ..., -0.625 ,  0.5625,  0.5   ],
    [-0.0625,  0.1875,  0.375 , ...,  0.0625,  0.0625, -0.125 ],
    [-0.125 , -0.8125, -0.5   , ..., -0.3125,  0.5625,  0.4375],
    ...,
    [ 0.625 ,  0.625 , -0.125 , ...,  0.125 , -0.0625,  0.6875],
    [-0.625 , -0.625 , -0.625 , ...,  0.125 , -0.0625,  0.6875],
    [ 0.    ,  0.    , -1.1875, ...,  0.125 ,  0.    ,  0.6875]])
    >>> aia.header.get('cunit1')
    'arcsec'
    >>> aia.show()
    >>> import matplotlib.cm as cm
    >>> import matplotlib.colors as colors
    >>> aia.show(cmap=cm.hot, norm=colors.Normalize(1, 2048))

    See Also
    --------
    numpy.ndarray Parent class for the Map object

    References
    ----------
    | http://docs.scipy.org/doc/numpy/reference/arrays.classes.html
    | http://docs.scipy.org/doc/numpy/user/basics.subclassing.html
    | http://docs.scipy.org/doc/numpy/reference/ufuncs.html
    | http://www.scipy.org/Subclasses

    """
    def __new__(cls, data, header=None): #pylint: disable=W0613
        """Creates a new BaseMap instance"""
        if isinstance(data, np.ndarray):
            obj = data.view(cls)
        elif isinstance(data, list):
            obj = np.asarray(data).view(cls)
        else:
            raise TypeError('Invalid data')

        return obj

    def __init__(self, data, header=None): #pylint: disable=W0613
        """BaseMap constructor"""
        if header:
            self.header = header
            self.date = None
            self.name = None
            self.cmap = None
            self.norm = None

            # Set object attributes dynamically
            for attr, value in list(self.get_properties(header).items()):
                setattr(self, attr, value)

            self.center = {
                "x": wcs.get_center(header, axis='x'),
                "y": wcs.get_center(header, axis='y')
            }
            self.scale = {
                "x": header.get('cdelt1'),
                "y": header.get('cdelt2')
            }
            self.units = {
                "x": wcs.get_units(header, axis='x'),
                "y": wcs.get_units(header, axis='y')
            }
            self.rsun = wcs.solar_limb(header)

    def __array_finalize__(self, obj):
        """Finishes instantiation of the new map object"""
        if obj is None:
            return

        if hasattr(obj, 'header'):
            self.header = obj.header

            # preserve object properties
            properties = self.get_properties(obj.header)
            for attr, value in list(properties.items()):
                setattr(self, attr, getattr(obj, attr, value))

            self.center = obj.center
            self.scale = obj.scale
            self.units = obj.units

    def __array_wrap__(self, out_arr, context=None):
        """Returns a wrapped instance of a Map object"""
        return np.ndarray.__array_wrap__(self, out_arr, context)

    def __add__(self, other):
        """Add two maps. Currently does not take into account the alignment
        between the two maps."""
        result = np.ndarray.__add__(self, other)

        return result

    def __getitem__(self, key):
        """Overiding indexing operation to ensure that header is updated"""
        if isinstance(key, tuple) and type(key[0]) is slice:
            x_range = [key[1].start, key[1].stop]
            y_range = [key[0].start, key[0].stop]

            return self.submap(y_range, x_range, units="pixels")
        else:
            return np.ndarray.__getitem__(self, key)

    def __sub__(self, other):
        """Subtract two maps. Currently does not take into account the alignment
        between the two maps."""
        result = np.ndarray.__sub__(self, other)

        minmax = np.array([abs(result.min()), abs(result.max())]).max()
        result.norm = colors.Normalize(-minmax, minmax, True)

        result.cmap = cm.gray #@UndefinedVariable

        return result

    @classmethod
    def get_properties(cls):
        """Returns default map properties"""
        return {
            'cmap': cm.gray,  #@UndefinedVariable
            'norm': colors.Normalize(5, 1024, True),
            'date': datetime.today(),
            'det': "None",
            'inst': "None",
            'meas': "None",
            'obs': "None",
            'name': "Default Map",
            'r_sun': None
        }

    @property
    def xrange(self):
        """Return the X range of the image in arcsec."""
        xmin = self.center['x'] - self.shape[1] / 2 * self.scale['x']
        xmax = self.center['x'] + self.shape[1] / 2 * self.scale['x']
        return [xmin, xmax]

    @property
    def yrange(self):
        """Return the Y range of the image in arcsec."""
        ymin = self.center['y'] - self.shape[0] / 2 * self.scale['y']
        ymax = self.center['y'] + self.shape[0] / 2 * self.scale['y']
        return [ymin, ymax]

    def arcsecs_to_pixels(self, value, dim):
        if dim not in ['x', 'y']:
            raise ValueError("Invalid dimension. Must be one of 'x' or 'y'.")
        size = self.shape[dim == 'y'] # 0 if dim == 'x', 1 if dim == 'y'.

        return value / self.scale[dim] + (size / 2)

    def submap(self, range_a, range_b, units="arcseconds"):
        """Returns a submap of the map with the specified range

        Parameters
        ----------
        range_a : list
            The range of data to select across either the x axis (if
            units='arcseconds') or the y axis (if units='pixels').
        range_b : list
            The range of data to select across either the y axis (if
            units='arcseconds') or the x axis (if units='pixels').
        units : {'arcseconds' | 'pixels'}, optional
            The units for which the submap region has been specified.

        Returns
        -------
        out : BaseMap
            A new map instance is returned representing to specified sub-region.

        Examples
        --------
        >>> aia.submap([-5,5],[-5,5])
        AIAMap([[ 341.3125,  266.5   ,  329.375 ,  330.5625,  298.875 ],
        [ 347.1875,  273.4375,  247.4375,  303.5   ,  305.3125],
        [ 322.8125,  302.3125,  298.125 ,  299.    ,  261.5   ],
        [ 334.875 ,  289.75  ,  269.25  ,  256.375 ,  242.3125],
        [ 273.125 ,  241.75  ,  248.8125,  263.0625,  249.0625]])

        >>> aia.submap([0,5],[0,5], units='pixels')
        AIAMap([[ 0.3125, -0.0625, -0.125 ,  0.    , -0.375 ],
        [ 1.    ,  0.1875, -0.8125,  0.125 ,  0.3125],
        [-1.1875,  0.375 , -0.5   ,  0.25  , -0.4375],
        [-0.6875, -0.3125,  0.8125,  0.0625,  0.1875],
        [-0.875 ,  0.25  ,  0.1875,  0.    , -0.6875]])
        """
        # Arcseconds => Pixels
        #  x_px = (x / cdelt1) + (width / 2)
        #
        if units is "arcseconds":
            x_pixels = [self.arcsecs_to_pixels(elem, 'x') for elem in range_a]
            y_pixels = [self.arcsecs_to_pixels(elem, 'y') for elem in range_b]
        elif units is "pixels":
            x_pixels = range_b
            y_pixels = range_a
        else:
            raise ValueError(
                "Invalid unit. Must be one of 'arcseconds' or 'pixels'")

        # Make a copy of the header with updated centering information
        header = self.header.copy()
        header['crpix1'] = header['crpix1'] - x_pixels[0]
        header['crpix2'] = header['crpix2'] - y_pixels[0]
        header['naxis1'] = x_pixels[1] - x_pixels[0]
        header['naxis2'] = y_pixels[1] - y_pixels[0]

        # Get ndarray representation of submap
        data = np.asarray(self)[y_pixels[0]:y_pixels[1],
                                x_pixels[0]:x_pixels[1]]

        return self.__class__(data, header)

    def plot(self, overlays=[], draw_limb=False, **matplot_args):
        """Plots the map object using matplotlib

        Parameters
        ----------
        draw_limb : bool
            Whether a circle should be drawn around the solar limb.
        **matplot_args : dict
            Matplotlib Any additional imshow arguments that should be used
            when plotting the image.
        """
        if draw_limb:
            overlays = overlays + [_draw_limb]
        # Create a figure and add title and axes
        fig = plt.figure()

        axes = fig.add_subplot(111)
        axes.set_title("%s %s" % (self.name, self.date))
        axes.set_xlabel('X-postion [' + self.units['x'] + ']')
        axes.set_ylabel('Y-postion [' + self.units['y'] + ']')

        # Determine extent
        extent = self.xrange + self.yrange

        # Matplotlib arguments
        params = {
            "cmap": self.cmap,
            "norm": self.norm
        }
        params.update(matplot_args)

        im = axes.imshow(self, origin='lower', extent=extent, **params)
        fig.colorbar(im)

        for overlay in overlays:
            fig, axes = overlay(self, fig, axes)
        return fig

    def show(self, overlays=[], **matplot_args):
        return self.plot(overlays, **matplot_args).show()

class UnrecognizedDataSouceError(ValueError):
    """Exception to raise when an unknown datasource is encountered"""
    pass
<|endoftext|>


<commit_before>from __future__ import absolute_import

import mongoext.abc
import mongoext.collection
import mongoext.schema
import mongoext.exc

class MetaSchema(type):
    def __new__(cls, class_name, bases, attrs):
        schema = {}
        for base in bases:
            for name, obj in vars(base).iteritems():
                if issubclass(type(obj), mongoext.schema.Descriptor):
                    schema[name] = obj
        for name, obj in attrs.iteritems():
            if issubclass(type(obj), mongoext.schema.Descriptor):
                schema[name] = obj

        attrs['__schema__'] = schema
        for name, descriptor in schema.items():
            attrs[name] = mongoext.abc.AbstractField(descriptor)
        return super(MetaSchema, cls).__new__(cls, class_name, bases, attrs)

class Document(object):
    __metaclass__ = MetaSchema
    __scheme__ = None

    _id = mongoext.schema.Field()

    def __init__(self, **data):
        data = mongoext.schema.process(self.__schema__, data, weak=True)
        for name, value in data.items():
            setattr(self, name, value)

    def __contains__(self, name):
        return name in self.__scheme__

    def __len__(self):
        return len(self.__scheme__)

    def __iter__(self):
        for name in self.__scheme__:
            yield name, getattr(self, name, None)

    def __hash__(self):
        return super(object, self).__hash__()

    def __repr__(self):
        return '<{}: {}>'.format(type(self).__name__, self._id)
<commit_msg>update hash function for document<commit_after>from __future__ import absolute_import

import mongoext.abc
import mongoext.collection
import mongoext.schema
import mongoext.exc

class MetaSchema(type):
    def __new__(cls, class_name, bases, attrs):
        schema = {}
        for base in bases:
            for name, obj in vars(base).iteritems():
                if issubclass(type(obj), mongoext.schema.Descriptor):
                    schema[name] = obj
        for name, obj in attrs.iteritems():
            if issubclass(type(obj), mongoext.schema.Descriptor):
                schema[name] = obj

        attrs['__schema__'] = schema
        for name, descriptor in schema.items():
            attrs[name] = mongoext.abc.AbstractField(descriptor)
        return super(MetaSchema, cls).__new__(cls, class_name, bases, attrs)

class Document(object):
    __metaclass__ = MetaSchema
    __scheme__ = None

    _id = mongoext.schema.Field()

    def __init__(self, **data):
        data = mongoext.schema.process(self.__schema__, data, weak=True)
        for name, value in data.items():
            setattr(self, name, value)

    def __contains__(self, name):
        return name in self.__scheme__

    def __len__(self):
        return len(self.__scheme__)

    def __iter__(self):
        for name in self.__scheme__:
            yield name, getattr(self, name, None)

    def __hash__(self):
        return id(self)

    def __repr__(self):
        return '<{}: {}>'.format(type(self).__name__, self._id)
<|endoftext|>


<commit_before>import sys

from numbers import Number
from pathlib import Path
from datetime import datetime
from logging import getLogger

try:
    from . import config
except ImportError as e:
    raise ImportError('Please copy config.example.py to config.py and customize it.') from e

sequence = (tuple, list)
path = (str, Path)
set_sequence = (tuple, list, set, frozenset)
set_sequence_range = (tuple, list, range, set, frozenset)

worker_count = config.GRID[0] * config.GRID[1]

_valid_types = {
    'ACCOUNTS': set_sequence,
    'ACCOUNTS_CSV': path,
    'ALT_PRECISION': int,
    'ALT_RANGE': sequence,
    'ALWAYS_NOTIFY': int,
    'ALWAYS_NOTIFY_IDS': set_sequence_range,
    'APP_SIMULATION': bool,
    'AREA_NAME': str,
    'AUTHKEY': bytes,
    'BOOTSTRAP_RADIUS': Number,
    'BOUNDARIES': object,
    'CACHE_CELLS': bool,
    'CAPTCHAS_ALLOWED': int,
    'CAPTCHA_KEY': str,
    'COMPLETE_TUTORIAL': bool,
    'COROUTINES_LIMIT': int,
    'DB': dict,
    'DB_ENGINE': str,
    'DIRECTORY': path,
    'DISCORD_INVITE_ID': str,
    'ENCOUNTER': str,
    'ENCOUNTER_IDS': set_sequence_range,
    'FAILURES_ALLOWED': int,
    'FAVOR_CAPTCHA': bool,
    'FB_PAGE_ID': str,
    'FIXED_OPACITY': bool,
    'FORCED_KILL': bool,
    'FULL_TIME': Number,
    'GIVE_UP_KNOWN': Number,
    'GIVE_UP_UNKNOWN': Number,
    'GOOD_ENOUGH': Number,
    'GOOGLE_MAPS_KEY': str,
    'GRID': sequence,
    'HASHTAGS': set_sequence,
    'HASH_KEY': (str,) + set_sequence,
    'HEATMAP': bool,
    'IGNORE_IVS': bool,
    'IGNORE_RARITY': bool,
    'IMAGE_STATS': bool,
    'INCUBATE_EGGS': bool,
    'INITIAL_SCORE': Number,
    'ITEM_LIMITS': dict,
    'IV_FONT': str,
    'LANDMARKS': object,
    'LANGUAGE': str,
    'LAST_MIGRATION': Number,
    'LOAD_CUSTOM_CSS_FILE': bool,
    'LOAD_CUSTOM_HTML_FILE': bool,
    'LOAD_CUSTOM_JS_FILE': bool,
    'LOGIN_TIMEOUT': Number,
    'MANAGER_ADDRESS': (str, tuple, list),
    'MAP_END': sequence,
    'MAP_FILTER_IDS': sequence,
    'MAP_PROVIDER_ATTRIBUTION': str,
    'MAP_PROVIDER_URL': str,
    'MAP_START': sequence,
    'MAP_WORKERS': bool,
    'MAX_CAPTCHAS': int,
    'MAX_RETRIES': int,
    'MINIMUM_RUNTIME': Number,
    'MINIMUM_SCORE': Number,
    'MORE_POINTS': bool,
    'MOVE_FONT': str,
    'NAME_FONT': str,
    'NEVER_NOTIFY_IDS': set_sequence_range,
    'NOTIFY': bool,
    'NOTIFY_IDS': set_sequence_range,
    'NOTIFY_RANKING': int,
    'PASS': str,
    'PB_API_KEY': str,
    'PB_CHANNEL': int,
    'PLAYER_LOCALE': dict,
    'PROVIDER': str,
    'PROXIES': set_sequence,
    'RARE_IDS': set_sequence_range,
    'RARITY_OVERRIDE': dict,
    'REFRESH_RATE': Number,
    'REPORT_MAPS': bool,
    'REPORT_SINCE': datetime,
    'RESCAN_UNKNOWN': Number,
    'SCAN_DELAY': Number,
    'SEARCH_SLEEP': Number,
    'SHOW_TIMER': bool,
    'SIMULTANEOUS_LOGINS': int,
    'SIMULTANEOUS_SIMULATION': int,
    'SKIP_SPAWN': Number,
    'SMART_THROTTLE': Number,
    'SPAWN_ID_INT': bool,
    'SPEED_LIMIT': Number,
    'SPEED_UNIT': str,
    'SPIN_COOLDOWN': Number,
    'SPIN_POKESTOPS': bool,
    'STAT_REFRESH': Number,
    'STAY_WITHIN_MAP': bool,
    'SWAP_OLDEST': Number,
    'TELEGRAM_BOT_TOKEN': str,
    'TELEGRAM_CHAT_ID': str,
    'TELEGRAM_USERNAME': str,
    'TIME_REQUIRED': Number,
    'TRASH_IDS': set_sequence_range,
    'TWEET_IMAGES': bool,
    'TWITTER_ACCESS_KEY': str,
    'TWITTER_ACCESS_SECRET': str,
    'TWITTER_CONSUMER_KEY': str,
    'TWITTER_CONSUMER_SECRET': str,
    'TWITTER_SCREEN_NAME': str,
    'TZ_OFFSET': Number,
    'UVLOOP': bool,
    'WEBHOOKS': set_sequence
}

_defaults = {
    'ACCOUNTS': None,
    'ACCOUNTS_CSV': None,
    'ALT_PRECISION': 2,
    'ALT_RANGE': (300, 400),
    'ALWAYS_NOTIFY': 0,
    'ALWAYS_NOTIFY_IDS': set(),
    'APP_SIMULATION': True,
    'AREA_NAME': 'Area',
    'AUTHKEY': b'm3wtw0',
    'BOOTSTRAP_RADIUS': 120,
    'BOUNDARIES': None,
    'CACHE_CELLS': False,
    'CAPTCHAS_ALLOWED': 3,
    'CAPTCHA_KEY': None,
    'COMPLETE_TUTORIAL': False,
    'CONTROL_SOCKS': None,
    'COROUTINES_LIMIT': worker_count,
    'DIRECTORY': '.',
    'DISCORD_INVITE_ID': None,
    'ENCOUNTER': None,
    'ENCOUNTER_IDS': None,
    'FAVOR_CAPTCHA': True,
    'FAILURES_ALLOWED': 2,
    'FB_PAGE_ID': None,
    'FIXED_OPACITY': False,
    'FORCED_KILL': None,
    'FULL_TIME': 1800,
    'GIVE_UP_KNOWN': 75,
    'GIVE_UP_UNKNOWN': 60,
    'GOOD_ENOUGH': 0.1,
    'GOOGLE_MAPS_KEY': '',
    'HASHTAGS': None,
    'IGNORE_IVS': False,
    'IGNORE_RARITY': False,
    'IMAGE_STATS': False,
    'INCUBATE_EGGS': True,
    'INITIAL_RANKING': None,
    'ITEM_LIMITS': None,
    'IV_FONT': 'monospace',
    'LANDMARKS': None,
    'LANGUAGE': 'EN',
    'LAST_MIGRATION': 1481932800,
    'LOAD_CUSTOM_CSS_FILE': False,
    'LOAD_CUSTOM_HTML_FILE': False,
    'LOAD_CUSTOM_JS_FILE': False,
    'LOGIN_TIMEOUT': 2.5,
    'MANAGER_ADDRESS': None,
    'MAP_FILTER_IDS': None,
    'MAP_PROVIDER_URL': '//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
    'MAP_PROVIDER_ATTRIBUTION': '© OpenStreetMap contributors',
    'MAP_WORKERS': True,
    'MAX_CAPTCHAS': 0,
    'MAX_RETRIES': 3,
    'MINIMUM_RUNTIME': 10,
    'MORE_POINTS': False,
    'MOVE_FONT': 'sans-serif',
    'NAME_FONT': 'sans-serif',
    'NEVER_NOTIFY_IDS': (),
    'NOTIFY': False,
    'NOTIFY_IDS': None,
    'NOTIFY_RANKING': None,
    'PASS': None,
    'PB_API_KEY': None,
    'PB_CHANNEL': None,
    'PLAYER_LOCALE': {'country': 'US', 'language': 'en', 'timezone': 'America/Denver'},
    'PROVIDER': None,
    'PROXIES': None,
    'RARE_IDS': (),
    'RARITY_OVERRIDE': {},
    'REFRESH_RATE': 0.6,
    'REPORT_MAPS': True,
    'REPORT_SINCE': None,
    'RESCAN_UNKNOWN': 90,
    'SCAN_DELAY': 10,
    'SEARCH_SLEEP': 2.5,
    'SHOW_TIMER': False,
    'SIMULTANEOUS_LOGINS': 2,
    'SIMULTANEOUS_SIMULATION': 4,
    'SKIP_SPAWN': 90,
    'SMART_THROTTLE': False,
    'SPAWN_ID_INT': True,
    'SPEED_LIMIT': 19.5,
    'SPEED_UNIT': 'miles',
    'SPIN_COOLDOWN': 300,
    'SPIN_POKESTOPS': True,
    'STAT_REFRESH': 5,
    'STAY_WITHIN_MAP': True,
    'SWAP_OLDEST': 21600 / worker_count,
    'TELEGRAM_BOT_TOKEN': None,
    'TELEGRAM_CHAT_ID': None,
    'TELEGRAM_USERNAME': None,
    'TIME_REQUIRED': 300,
    'TRASH_IDS': (),
    'TWEET_IMAGES': False,
    'TWITTER_ACCESS_KEY': None,
    'TWITTER_ACCESS_SECRET': None,
    'TWITTER_CONSUMER_KEY': None,
    'TWITTER_CONSUMER_SECRET': None,
    'TWITTER_SCREEN_NAME': None,
    'TZ_OFFSET': None,
    'UVLOOP': True,
    'WEBHOOKS': None,
    'LURE_ON_DEMAND': False
}

class Config:
    __spec__ = __spec__
    __slots__ = tuple(_valid_types.keys()) + ('log',)

    def __init__(self):
        self.log = getLogger('sanitizer')
        for key, value in (x for x in vars(config).items() if x[0].isupper()):
            try:
                if isinstance(value, _valid_types[key]):
                    setattr(self, key, value)
                    if key in _defaults:
                        del _defaults[key]
                elif key in _defaults and value is _defaults[key]:
                    setattr(self, key, _defaults.pop(key))
                else:
                    valid = _valid_types[key]
                    actual = type(value).__name__
                    if isinstance(valid, type):
                        err = '{} must be {}. Yours is: {}.'.format(
                            key, valid.__name__, actual)
                    else:
                        types = ', '.join((x.__name__ for x in valid))
                        err = '{} must be one of {}. Yours is: {}'.format(
                            key, types, actual)
                    raise TypeError(err)
            except KeyError:
                self.log.warning('{} is not a valid config option'.format(key))

    def __getattr__(self, name):
        try:
            default = _defaults.pop(name)
            setattr(self, name, default)
            return default
        except KeyError:
            if name == '__path__':
                return
            err = '{} not in config, and no default has been set.'.format(name)
            self.log.error(err)
            raise AttributeError(err)

sys.modules[__name__] = Config()

del _valid_types, config
<commit_msg><commit_after>import sys

from numbers import Number
from pathlib import Path
from datetime import datetime
from logging import getLogger

try:
    from . import config
except ImportError as e:
    raise ImportError('Please copy config.example.py to config.py and customize it.') from e

sequence = (tuple, list)
path = (str, Path)
set_sequence = (tuple, list, set, frozenset)
set_sequence_range = (tuple, list, range, set, frozenset)

worker_count = config.GRID[0] * config.GRID[1]

_valid_types = {
    'ACCOUNTS': set_sequence,
    'ACCOUNTS_CSV': path,
    'ALT_PRECISION': int,
    'ALT_RANGE': sequence,
    'ALWAYS_NOTIFY': int,
    'ALWAYS_NOTIFY_IDS': set_sequence_range,
    'APP_SIMULATION': bool,
    'AREA_NAME': str,
    'AUTHKEY': bytes,
    'BOOTSTRAP_RADIUS': Number,
    'BOUNDARIES': object,
    'CACHE_CELLS': bool,
    'CAPTCHAS_ALLOWED': int,
    'CAPTCHA_KEY': str,
    'COMPLETE_TUTORIAL': bool,
    'COROUTINES_LIMIT': int,
    'DB': dict,
    'DB_ENGINE': str,
    'DIRECTORY': path,
    'DISCORD_INVITE_ID': str,
    'ENCOUNTER': str,
    'ENCOUNTER_IDS': set_sequence_range,
    'FAILURES_ALLOWED': int,
    'FAVOR_CAPTCHA': bool,
    'FB_PAGE_ID': str,
    'FIXED_OPACITY': bool,
    'FORCED_KILL': bool,
    'FULL_TIME': Number,
    'GIVE_UP_KNOWN': Number,
    'GIVE_UP_UNKNOWN': Number,
    'GOOD_ENOUGH': Number,
    'GOOGLE_MAPS_KEY': str,
    'GRID': sequence,
    'HASHTAGS': set_sequence,
    'HASH_KEY': (str,) + set_sequence,
    'HEATMAP': bool,
    'IGNORE_IVS': bool,
    'IGNORE_RARITY': bool,
    'IMAGE_STATS': bool,
    'INCUBATE_EGGS': bool,
    'INITIAL_SCORE': Number,
    'ITEM_LIMITS': dict,
    'IV_FONT': str,
    'LANDMARKS': object,
    'LANGUAGE': str,
    'LAST_MIGRATION': Number,
    'LOAD_CUSTOM_CSS_FILE': bool,
    'LOAD_CUSTOM_HTML_FILE': bool,
    'LOAD_CUSTOM_JS_FILE': bool,
    'LOGIN_TIMEOUT': Number,
    'MANAGER_ADDRESS': (str, tuple, list),
    'MAP_END': sequence,
    'MAP_FILTER_IDS': sequence,
    'MAP_PROVIDER_ATTRIBUTION': str,
    'MAP_PROVIDER_URL': str,
    'MAP_START': sequence,
    'MAP_WORKERS': bool,
    'MAX_CAPTCHAS': int,
    'MAX_RETRIES': int,
    'MINIMUM_RUNTIME': Number,
    'MINIMUM_SCORE': Number,
    'MORE_POINTS': bool,
    'MOVE_FONT': str,
    'NAME_FONT': str,
    'NEVER_NOTIFY_IDS': set_sequence_range,
    'NOTIFY': bool,
    'NOTIFY_IDS': set_sequence_range,
    'NOTIFY_RANKING': int,
    'PASS': str,
    'PB_API_KEY': str,
    'PB_CHANNEL': int,
    'PLAYER_LOCALE': dict,
    'PROVIDER': str,
    'PROXIES': set_sequence,
    'RARE_IDS': set_sequence_range,
    'RARITY_OVERRIDE': dict,
    'REFRESH_RATE': Number,
    'REPORT_MAPS': bool,
    'REPORT_SINCE': datetime,
    'RESCAN_UNKNOWN': Number,
    'SCAN_DELAY': Number,
    'SEARCH_SLEEP': Number,
    'SHOW_TIMER': bool,
    'SIMULTANEOUS_LOGINS': int,
    'SIMULTANEOUS_SIMULATION': int,
    'SKIP_SPAWN': Number,
    'SMART_THROTTLE': Number,
    'SPAWN_ID_INT': bool,
    'SPEED_LIMIT': Number,
    'SPEED_UNIT': str,
    'SPIN_COOLDOWN': Number,
    'SPIN_POKESTOPS': bool,
    'STAT_REFRESH': Number,
    'STAY_WITHIN_MAP': bool,
    'SWAP_OLDEST': Number,
    'TELEGRAM_BOT_TOKEN': str,
    'TELEGRAM_CHAT_ID': str,
    'TELEGRAM_USERNAME': str,
    'TIME_REQUIRED': Number,
    'TRASH_IDS': set_sequence_range,
    'TWEET_IMAGES': bool,
    'TWITTER_ACCESS_KEY': str,
    'TWITTER_ACCESS_SECRET': str,
    'TWITTER_CONSUMER_KEY': str,
    'TWITTER_CONSUMER_SECRET': str,
    'TWITTER_SCREEN_NAME': str,
    'TZ_OFFSET': Number,
    'UVLOOP': bool,
    'WEBHOOKS': set_sequence,
	'LURE_ON_DEMAND': bool
}

_defaults = {
    'ACCOUNTS': None,
    'ACCOUNTS_CSV': None,
    'ALT_PRECISION': 2,
    'ALT_RANGE': (300, 400),
    'ALWAYS_NOTIFY': 0,
    'ALWAYS_NOTIFY_IDS': set(),
    'APP_SIMULATION': True,
    'AREA_NAME': 'Area',
    'AUTHKEY': b'm3wtw0',
    'BOOTSTRAP_RADIUS': 120,
    'BOUNDARIES': None,
    'CACHE_CELLS': False,
    'CAPTCHAS_ALLOWED': 3,
    'CAPTCHA_KEY': None,
    'COMPLETE_TUTORIAL': False,
    'CONTROL_SOCKS': None,
    'COROUTINES_LIMIT': worker_count,
    'DIRECTORY': '.',
    'DISCORD_INVITE_ID': None,
    'ENCOUNTER': None,
    'ENCOUNTER_IDS': None,
    'FAVOR_CAPTCHA': True,
    'FAILURES_ALLOWED': 2,
    'FB_PAGE_ID': None,
    'FIXED_OPACITY': False,
    'FORCED_KILL': None,
    'FULL_TIME': 1800,
    'GIVE_UP_KNOWN': 75,
    'GIVE_UP_UNKNOWN': 60,
    'GOOD_ENOUGH': 0.1,
    'GOOGLE_MAPS_KEY': '',
    'HASHTAGS': None,
    'IGNORE_IVS': False,
    'IGNORE_RARITY': False,
    'IMAGE_STATS': False,
    'INCUBATE_EGGS': True,
    'INITIAL_RANKING': None,
    'ITEM_LIMITS': None,
    'IV_FONT': 'monospace',
    'LANDMARKS': None,
    'LANGUAGE': 'EN',
    'LAST_MIGRATION': 1481932800,
    'LOAD_CUSTOM_CSS_FILE': False,
    'LOAD_CUSTOM_HTML_FILE': False,
    'LOAD_CUSTOM_JS_FILE': False,
    'LOGIN_TIMEOUT': 2.5,
    'MANAGER_ADDRESS': None,
    'MAP_FILTER_IDS': None,
    'MAP_PROVIDER_URL': '//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
    'MAP_PROVIDER_ATTRIBUTION': '© OpenStreetMap contributors',
    'MAP_WORKERS': True,
    'MAX_CAPTCHAS': 0,
    'MAX_RETRIES': 3,
    'MINIMUM_RUNTIME': 10,
    'MORE_POINTS': False,
    'MOVE_FONT': 'sans-serif',
    'NAME_FONT': 'sans-serif',
    'NEVER_NOTIFY_IDS': (),
    'NOTIFY': False,
    'NOTIFY_IDS': None,
    'NOTIFY_RANKING': None,
    'PASS': None,
    'PB_API_KEY': None,
    'PB_CHANNEL': None,
    'PLAYER_LOCALE': {'country': 'US', 'language': 'en', 'timezone': 'America/Denver'},
    'PROVIDER': None,
    'PROXIES': None,
    'RARE_IDS': (),
    'RARITY_OVERRIDE': {},
    'REFRESH_RATE': 0.6,
    'REPORT_MAPS': True,
    'REPORT_SINCE': None,
    'RESCAN_UNKNOWN': 90,
    'SCAN_DELAY': 10,
    'SEARCH_SLEEP': 2.5,
    'SHOW_TIMER': False,
    'SIMULTANEOUS_LOGINS': 2,
    'SIMULTANEOUS_SIMULATION': 4,
    'SKIP_SPAWN': 90,
    'SMART_THROTTLE': False,
    'SPAWN_ID_INT': True,
    'SPEED_LIMIT': 19.5,
    'SPEED_UNIT': 'miles',
    'SPIN_COOLDOWN': 300,
    'SPIN_POKESTOPS': True,
    'STAT_REFRESH': 5,
    'STAY_WITHIN_MAP': True,
    'SWAP_OLDEST': 21600 / worker_count,
    'TELEGRAM_BOT_TOKEN': None,
    'TELEGRAM_CHAT_ID': None,
    'TELEGRAM_USERNAME': None,
    'TIME_REQUIRED': 300,
    'TRASH_IDS': (),
    'TWEET_IMAGES': False,
    'TWITTER_ACCESS_KEY': None,
    'TWITTER_ACCESS_SECRET': None,
    'TWITTER_CONSUMER_KEY': None,
    'TWITTER_CONSUMER_SECRET': None,
    'TWITTER_SCREEN_NAME': None,
    'TZ_OFFSET': None,
    'UVLOOP': True,
    'WEBHOOKS': None,
    'LURE_ON_DEMAND': False
}

class Config:
    __spec__ = __spec__
    __slots__ = tuple(_valid_types.keys()) + ('log',)

    def __init__(self):
        self.log = getLogger('sanitizer')
        for key, value in (x for x in vars(config).items() if x[0].isupper()):
            try:
                if isinstance(value, _valid_types[key]):
                    setattr(self, key, value)
                    if key in _defaults:
                        del _defaults[key]
                elif key in _defaults and value is _defaults[key]:
                    setattr(self, key, _defaults.pop(key))
                else:
                    valid = _valid_types[key]
                    actual = type(value).__name__
                    if isinstance(valid, type):
                        err = '{} must be {}. Yours is: {}.'.format(
                            key, valid.__name__, actual)
                    else:
                        types = ', '.join((x.__name__ for x in valid))
                        err = '{} must be one of {}. Yours is: {}'.format(
                            key, types, actual)
                    raise TypeError(err)
            except KeyError:
                self.log.warning('{} is not a valid config option'.format(key))

    def __getattr__(self, name):
        try:
            default = _defaults.pop(name)
            setattr(self, name, default)
            return default
        except KeyError:
            if name == '__path__':
                return
            err = '{} not in config, and no default has been set.'.format(name)
            self.log.error(err)
            raise AttributeError(err)

sys.modules[__name__] = Config()

del _valid_types, config
<|endoftext|>