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

# Some parts of this software was developed by employees of
# the National Institute of Standards and Technology (NIST),
# an agency of the Federal Government.
# Pursuant to title 17 United States Code Section 105, works of NIST employees
# are not subject to copyright protection in the United States and are
# considered to be in the public domain. Permission to freely use, copy,
# modify, and distribute this software and its documentation without fee
# is hereby granted, provided that this notice and disclaimer of warranty
# appears in all copies.

# THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
# EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
# THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
# INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
# SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT
# SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
# INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
# OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON
# WARRANTY, CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED
# BY PERSONS OR PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED
# FROM, OR AROSE OUT OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES
# PROVIDED HEREUNDER.

# Distributions of NIST software should also include copyright and licensing
# statements of any third-party software that are legally bundled with the
# code in compliance with the conditions of those licenses.

"""A fake implementation of SasInterface, based on v1.0 of the SAS-CBSD TS.

A local test server could be run by using "python fake_sas.py".

"""

from BaseHTTPServer import BaseHTTPRequestHandler
from BaseHTTPServer import HTTPServer
from datetime import datetime
from datetime import timedelta
import uuid
import json
import ssl
import os
import sas_interface

# Fake SAS server configurations.
PORT = 9000
CERT_FILE = 'certs/server.cert'
KEY_FILE = 'certs/server.key'
CA_CERT = 'certs/ca.cert'
CIPHERS = [
    'AES128-GCM-SHA256',              # TLS_RSA_WITH_AES_128_GCM_SHA256
    'AES256-GCM-SHA384',              # TLS_RSA_WITH_AES_256_GCM_SHA384
    'ECDHE-ECDSA-AES128-GCM-SHA256',  # TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
    'ECDHE-ECDSA-AES256-GCM-SHA384',  # TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
    'ECDHE-RSA-AES128-GCM-SHA256',    # TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
]

MISSING_PARAM = 102
INVALID_PARAM = 103

class FakeSas(sas_interface.SasInterface):
  """A fake implementation of SasInterface.

  Returns success for all requests with plausible fake values for all required
  response fields.

  """

  def __init__(self):
    pass

  def Registration(self, request, ssl_cert=None, ssl_key=None):
    response = {'registrationResponse': []}
    for req in request['registrationRequest']:
      if 'fccId' not in req or 'cbsdSerialNumber' not in req:
        response['registrationResponse'].append({
            'response': self._GetSuccessResponse()
        })
        continue
      response['registrationResponse'].append({
        'cbsdId': req['fccId'] + '/' + req['cbsdSerialNumber'],
        'response': self._GetSuccessResponse()
    })
    return response

  def SpectrumInquiry(self, request, ssl_cert=None, ssl_key=None):
    response = {'spectrumInquiryResponse': []}
    for req in request['spectrumInquiryRequest']:
      response['spectrumInquiryResponse'].append({
          'cbsdId': req['cbsdId'],
          'availableChannel': {
              'frequencyRange': {
                  'lowFrequency': 3620000000,
                  'highFrequency': 3630000000
              },
              'channelType': 'GAA',
              'ruleApplied': 'FCC_PART_96'
          },
          'response': self._GetSuccessResponse()
      })
    return response

  def Grant(self, request, ssl_cert=None, ssl_key=None):
    response = {'grantResponse': []}
    for req in request['grantRequest']:
      if ('cbsdId' not in req) :
        response['grantResponse'].append({
          'response': self._GetMissingParamResponse()
        })
      else :
        if (('highFrequency' not in req['operationParam']['operationFrequencyRange']) or \
           ('lowFrequency' not in req['operationParam']['operationFrequencyRange'])) :
           response['grantResponse'].append({
             'cbsdId': req['cbsdId'],
             'response': self._GetMissingParamResponse()
           })
        else:
          response['grantResponse'].append({
            'cbsdId': req['cbsdId'],
            'grantId': 'fake_grant_id_%s' % datetime.utcnow().isoformat(),
            'channelType': 'GAA',
            'response': self._GetSuccessResponse()
          })
    return response

  def Heartbeat(self, request, ssl_cert=None, ssl_key=None):
    response = {'heartbeatResponse': []}
    for req in request['heartbeatRequest']:
      transmit_expire_time = datetime.utcnow().replace(
          microsecond=0) + timedelta(minutes=1)
      response['heartbeatResponse'].append({
          'cbsdId': req['cbsdId'],
          'grantId': req['grantId'],
          'transmitExpireTime': transmit_expire_time.isoformat() + 'Z',
          'response': self._GetSuccessResponse()
      })
    return response

  def Relinquishment(self, request, ssl_cert=None, ssl_key=None):
    response = {'relinquishmentResponse': []}
    for req in request['relinquishmentRequest']:
      response['relinquishmentResponse'].append({
          'cbsdId': req['cbsdId'],
          'grantId': req['grantId'],
          'response': self._GetSuccessResponse()
      })
    return response

  def Deregistration(self, request, ssl_cert=None, ssl_key=None):
    response = {'deregistrationResponse': []}
    for req in request['deregistrationRequest']:
      if ('cbsdId' not in req) :
        response['deregistrationResponse'].append({
          'response': self._GetMissingParamResponse()
        })
      else :
        response['deregistrationResponse'].append({
          'cbsdId': req['cbsdId'],
          'response': self._GetSuccessResponse()
        })
    return response

  def GetSasImplementationRecord(self, request, ssl_cert=None, ssl_key=None):
    # Get the Sas implementation record
    impl_record = json.load(
      open(os.path.join('testcases', 'testdata', 'impl_record_0.json')))
    if request == impl_record['id']:
      return impl_record
    else:
      # Return Empty if invalid Id
      return {}

  def GetEscSensorRecord(self, request, ssl_cert=None, ssl_key=None):
    # Get the Esc Sensor record
    esc_sensor_record = json.load(
      open(os.path.join('testcases', 'testdata', 'esc_sensor_record_0.json')))
    if request == esc_sensor_record['id']:
      return esc_sensor_record
    else:
      # Return Empty if invalid Id
      return {}

  def _GetSuccessResponse(self):
    return {'responseCode': 0}

  def _GetMissingParamResponse(self):
    return {'responseCode': MISSING_PARAM}

class FakeSasAdmin(sas_interface.SasAdminInterface):
  """Implementation of SAS Admin for Fake SAS."""
  def Reset(self):
    pass

  def InjectFccId(self, request):
    pass

  def InjectUserId(self, request):
    pass

  def BlacklistByFccId(self, request):
    pass

  def BlacklistByFccIdAndSerialNumber(self, request):
    pass

  def PreloadRegistrationData(self, request):
    pass

  def InjectZoneData(self, request, ssl_cert=None, ssl_key=None):
    return request['record']['id']

  def InjectPalDatabaseRecord(self, request):
    pass

  def InjectFss(self, request):
    pass

  def InjectWisp(self, request):
    pass

  def InjectSasAdministratorRecord(self, request):
    pass

  def InjectSasImplementationRecord(self, request):
    pass

  def InjectEscSensorDataRecord(self, request):
    pass

  def TriggerMeasurementReportRegistration(self, request):
    pass

  def TriggerMeasurementReportHeartbeat(self, request):
    pass

  def TriggerPpaCreation(self, request, ssl_cert=None, ssl_key=None):
    return 'zone/ppa/fake_sas/%s/%s' % (request['palIds'][0]['palId'],
                                        uuid.uuid4().hex)

  def TriggerDailyActivitiesImmediately(self):
    pass

  def GetDailyActivitiesStatus(self):
    return {'completed': True}

class FakeSasHandler(BaseHTTPRequestHandler):
  def _parseUrl(self, url):
    """Parse the Url into the path and value"""
    splitted_url = url.split('/')[1:]
    # Returns path and value
    return '/'.join(splitted_url[0:2]), '/'.join(splitted_url[2:])

  def do_POST(self):
    """Handles POST requests."""

    length = int(self.headers.getheader('content-length'))
    if length > 0:
      request = json.loads(self.rfile.read(length))
    if self.path == '/v1.0/registration':
      response = FakeSas().Registration(request)
    elif self.path == '/v1.0/spectrumInquiry':
      response = FakeSas().SpectrumInquiry(request)
    elif self.path == '/v1.0/grant':
      response = FakeSas().Grant(request)
    elif self.path == '/v1.0/heartbeat':
      response = FakeSas().Heartbeat(request)
    elif self.path == '/v1.0/relinquishment':
      response = FakeSas().Relinquishment(request)
    elif self.path == '/v1.0/deregistration':
      response = FakeSas().Deregistration(request)
    elif self.path == '/admin/injectdata/zone':
      response = FakeSasAdmin().InjectZoneData(request)
    elif self.path == 'admin/trigger/create_ppa':
      response = FakeSasAdmin().TriggerPpaCreation(request)
    elif self.path == 'admin/get_daily_activities_status':
      response = FakeSasAdmin().GetDailyActivitiesStatus()
    elif self.path in ('/admin/reset', '/admin/injectdata/fcc_id',
                       '/admin/injectdata/user_id',
                       '/admin/injectdata/conditional_registration',
                       '/admin/injectdata/blacklist_fcc_id',
                       '/admin/injectdata/blacklist_fcc_id_and_serial_number',
                       '/admin/injectdata/fss', '/admin/injectdata/wisp',
                       '/admin/injectdata/cluster_list',
                       '/admin/injectdata/pal_database_record',
                       '/admin/injectdata/sas_admin',
                       '/admin/injectdata/sas_impl',
                       '/admin/injectdata/esc_sensor',
                       '/admin/trigger/meas_report_in_registration_response',
                       '/admin/trigger/meas_report_in_heartbeat_response',
                       '/admin/trigger/daily_activities_immediately'):
      response = ''
    else:
      self.send_response(404)
      return
    self.send_response(200)
    self.send_header('Content-type', 'application/json')
    self.end_headers()
    self.wfile.write(json.dumps(response))

  def do_GET(self):
    """Handles GET requests."""
    path, value = self._parseUrl(self.path)
    if path == "v1.0/sas_impl":
     response = FakeSas().GetSasImplementationRecord(value)
    elif path == "v1.0/esc_sensor":
      response = FakeSas().GetEscSensorRecord(value)
    else:
      self.send_response(404)
      return
    self.send_response(200)
    self.send_header('Content-type', 'application/json')
    self.end_headers()
    self.wfile.write(json.dumps(response))

if __name__ == '__main__':
  server = HTTPServer(('localhost', PORT), FakeSasHandler)
  server.socket = ssl.wrap_socket(
      server.socket,
      certfile=CERT_FILE,
      keyfile=KEY_FILE,
      ca_certs=CA_CERT,
      cert_reqs=ssl.CERT_REQUIRED,  # CERT_NONE to disable client certificate check
      ssl_version=ssl.PROTOCOL_TLSv1_2,
      ciphers=':'.join(CIPHERS),
      server_side=True)

  print 'Will start server at localhost:%d, use <Ctrl-C> to stop.' % PORT
  server.serve_forever()
<commit_msg>Update fake_sas.py<commit_after>#    Copyright 2016 SAS Project Authors. All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License");
#    you may not use this file except in compliance with the License.
#    You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.

# Some parts of this software was developed by employees of
# the National Institute of Standards and Technology (NIST),
# an agency of the Federal Government.
# Pursuant to title 17 United States Code Section 105, works of NIST employees
# are not subject to copyright protection in the United States and are
# considered to be in the public domain. Permission to freely use, copy,
# modify, and distribute this software and its documentation without fee
# is hereby granted, provided that this notice and disclaimer of warranty
# appears in all copies.

# THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
# EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
# THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
# INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
# SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT
# SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
# INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
# OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON
# WARRANTY, CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED
# BY PERSONS OR PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED
# FROM, OR AROSE OUT OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES
# PROVIDED HEREUNDER.

# Distributions of NIST software should also include copyright and licensing
# statements of any third-party software that are legally bundled with the
# code in compliance with the conditions of those licenses.

"""A fake implementation of SasInterface, based on v1.0 of the SAS-CBSD TS.

A local test server could be run by using "python fake_sas.py".

"""

from BaseHTTPServer import BaseHTTPRequestHandler
from BaseHTTPServer import HTTPServer
from datetime import datetime
from datetime import timedelta
import uuid
import json
import ssl
import os
import sas_interface

# Fake SAS server configurations.
PORT = 9000
CERT_FILE = 'certs/server.cert'
KEY_FILE = 'certs/server.key'
CA_CERT = 'certs/ca.cert'
CIPHERS = [
    'AES128-GCM-SHA256',              # TLS_RSA_WITH_AES_128_GCM_SHA256
    'AES256-GCM-SHA384',              # TLS_RSA_WITH_AES_256_GCM_SHA384
    'ECDHE-ECDSA-AES128-GCM-SHA256',  # TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
    'ECDHE-ECDSA-AES256-GCM-SHA384',  # TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
    'ECDHE-RSA-AES128-GCM-SHA256',    # TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
]

MISSING_PARAM = 102
INVALID_PARAM = 103

class FakeSas(sas_interface.SasInterface):
  """A fake implementation of SasInterface.

  Returns success for all requests with plausible fake values for all required
  response fields.

  """

  def __init__(self):
    pass

  def Registration(self, request, ssl_cert=None, ssl_key=None):
    response = {'registrationResponse': []}
    for req in request['registrationRequest']:
      if 'fccId' not in req or 'cbsdSerialNumber' not in req:
        response['registrationResponse'].append({
            'response': self._GetSuccessResponse()
        })
        continue
      response['registrationResponse'].append({
        'cbsdId': req['fccId'] + '/' + req['cbsdSerialNumber'],
        'response': self._GetSuccessResponse()
    })
    return response

  def SpectrumInquiry(self, request, ssl_cert=None, ssl_key=None):
    response = {'spectrumInquiryResponse': []}
    for req in request['spectrumInquiryRequest']:
      response['spectrumInquiryResponse'].append({
          'cbsdId': req['cbsdId'],
          'availableChannel': {
              'frequencyRange': {
                  'lowFrequency': 3620000000,
                  'highFrequency': 3630000000
              },
              'channelType': 'GAA',
              'ruleApplied': 'FCC_PART_96'
          },
          'response': self._GetSuccessResponse()
      })
    return response

  def Grant(self, request, ssl_cert=None, ssl_key=None):
    response = {'grantResponse': []}
    for req in request['grantRequest']:
      if ('cbsdId' not in req) :
        response['grantResponse'].append({
          'response': self._GetMissingParamResponse()
        })
      else :
        if (('highFrequency' not in req['operationParam']['operationFrequencyRange']) or \
           ('lowFrequency' not in req['operationParam']['operationFrequencyRange'])) :
           response['grantResponse'].append({
             'cbsdId': req['cbsdId'],
             'response': self._GetMissingParamResponse()
           })
        else:
          response['grantResponse'].append({
            'cbsdId': req['cbsdId'],
            'grantId': 'fake_grant_id_%s' % datetime.utcnow().isoformat(),
            'channelType': 'GAA',
            'response': self._GetSuccessResponse()
          })
    return response

  def Heartbeat(self, request, ssl_cert=None, ssl_key=None):
    response = {'heartbeatResponse': []}
    for req in request['heartbeatRequest']:
      transmit_expire_time = datetime.utcnow().replace(
          microsecond=0) + timedelta(minutes=1)
      response['heartbeatResponse'].append({
          'cbsdId': req['cbsdId'],
          'grantId': req['grantId'],
          'transmitExpireTime': transmit_expire_time.isoformat() + 'Z',
          'response': self._GetSuccessResponse()
      })
    return response

  def Relinquishment(self, request, ssl_cert=None, ssl_key=None):
    response = {'relinquishmentResponse': []}
    for req in request['relinquishmentRequest']:
      response['relinquishmentResponse'].append({
          'cbsdId': req['cbsdId'],
          'grantId': req['grantId'],
          'response': self._GetSuccessResponse()
      })
    return response

  def Deregistration(self, request, ssl_cert=None, ssl_key=None):
    response = {'deregistrationResponse': []}
    for req in request['deregistrationRequest']:
      if ('cbsdId' not in req) :
        response['deregistrationResponse'].append({
          'response': self._GetMissingParamResponse()
        })
      else :
        response['deregistrationResponse'].append({
          'cbsdId': req['cbsdId'],
          'response': self._GetSuccessResponse()
        })
    return response

  def GetSasImplementationRecord(self, request, ssl_cert=None, ssl_key=None):
    # Get the Sas implementation record
    impl_record = json.load(
      open(os.path.join('testcases', 'testdata', 'impl_record_0.json')))
    if request == impl_record['id']:
      return impl_record
    else:
      # Return Empty if invalid Id
      return {}

  def GetEscSensorRecord(self, request, ssl_cert=None, ssl_key=None):
    # Get the Esc Sensor record
    esc_sensor_record = json.load(
      open(os.path.join('testcases', 'testdata', 'esc_sensor_record_0.json')))
    if request == esc_sensor_record['id']:
      return esc_sensor_record
    else:
      # Return Empty if invalid Id
      return {}

  def _GetSuccessResponse(self):
    return {'responseCode': 0}

  def _GetMissingParamResponse(self):
    return {'responseCode': MISSING_PARAM}

class FakeSasAdmin(sas_interface.SasAdminInterface):
  """Implementation of SAS Admin for Fake SAS."""
  def Reset(self):
    pass

  def InjectFccId(self, request):
    pass

  def InjectUserId(self, request):
    pass

  def BlacklistByFccId(self, request):
    pass

  def BlacklistByFccIdAndSerialNumber(self, request):
    pass

  def PreloadRegistrationData(self, request):
    pass

  def InjectZoneData(self, request, ssl_cert=None, ssl_key=None):
    return request['record']['id']

  def InjectPalDatabaseRecord(self, request):
    pass

  def InjectFss(self, request):
    pass

  def InjectWisp(self, request):
    pass

  def InjectSasAdministratorRecord(self, request):
    pass

  def InjectSasImplementationRecord(self, request):
    pass

  def InjectEscSensorDataRecord(self, request):
    pass

  def TriggerMeasurementReportRegistration(self, request):
    pass

  def TriggerMeasurementReportHeartbeat(self, request):
    pass

  def TriggerPpaCreation(self, request, ssl_cert=None, ssl_key=None):
    return 'zone/ppa/fake_sas/%s/%s' % (request['palIds'][0]['palId'],
                                        uuid.uuid4().hex)

  def TriggerDailyActivitiesImmediately(self):
    pass

  def GetDailyActivitiesStatus(self):
    return {'completed': True}

  def InjectCpiUser(self, request):
    pass

class FakeSasHandler(BaseHTTPRequestHandler):
  def _parseUrl(self, url):
    """Parse the Url into the path and value"""
    splitted_url = url.split('/')[1:]
    # Returns path and value
    return '/'.join(splitted_url[0:2]), '/'.join(splitted_url[2:])

  def do_POST(self):
    """Handles POST requests."""

    length = int(self.headers.getheader('content-length'))
    if length > 0:
      request = json.loads(self.rfile.read(length))
    if self.path == '/v1.0/registration':
      response = FakeSas().Registration(request)
    elif self.path == '/v1.0/spectrumInquiry':
      response = FakeSas().SpectrumInquiry(request)
    elif self.path == '/v1.0/grant':
      response = FakeSas().Grant(request)
    elif self.path == '/v1.0/heartbeat':
      response = FakeSas().Heartbeat(request)
    elif self.path == '/v1.0/relinquishment':
      response = FakeSas().Relinquishment(request)
    elif self.path == '/v1.0/deregistration':
      response = FakeSas().Deregistration(request)
    elif self.path == '/admin/injectdata/zone':
      response = FakeSasAdmin().InjectZoneData(request)
    elif self.path == 'admin/trigger/create_ppa':
      response = FakeSasAdmin().TriggerPpaCreation(request)
    elif self.path == 'admin/get_daily_activities_status':
      response = FakeSasAdmin().GetDailyActivitiesStatus()
    elif self.path in ('/admin/reset', '/admin/injectdata/fcc_id',
                       '/admin/injectdata/user_id',
                       '/admin/injectdata/conditional_registration',
                       '/admin/injectdata/blacklist_fcc_id',
                       '/admin/injectdata/blacklist_fcc_id_and_serial_number',
                       '/admin/injectdata/fss', '/admin/injectdata/wisp',
                       '/admin/injectdata/cluster_list',
                       '/admin/injectdata/pal_database_record',
                       '/admin/injectdata/sas_admin',
                       '/admin/injectdata/sas_impl',
                       '/admin/injectdata/esc_sensor',
                       '/admin/trigger/meas_report_in_registration_response',
                       '/admin/trigger/meas_report_in_heartbeat_response',
                       '/admin/trigger/daily_activities_immediately'):
      response = ''
    else:
      self.send_response(404)
      return
    self.send_response(200)
    self.send_header('Content-type', 'application/json')
    self.end_headers()
    self.wfile.write(json.dumps(response))

  def do_GET(self):
    """Handles GET requests."""
    path, value = self._parseUrl(self.path)
    if path == "v1.0/sas_impl":
     response = FakeSas().GetSasImplementationRecord(value)
    elif path == "v1.0/esc_sensor":
      response = FakeSas().GetEscSensorRecord(value)
    else:
      self.send_response(404)
      return
    self.send_response(200)
    self.send_header('Content-type', 'application/json')
    self.end_headers()
    self.wfile.write(json.dumps(response))

if __name__ == '__main__':
  server = HTTPServer(('localhost', PORT), FakeSasHandler)
  server.socket = ssl.wrap_socket(
      server.socket,
      certfile=CERT_FILE,
      keyfile=KEY_FILE,
      ca_certs=CA_CERT,
      cert_reqs=ssl.CERT_REQUIRED,  # CERT_NONE to disable client certificate check
      ssl_version=ssl.PROTOCOL_TLSv1_2,
      ciphers=':'.join(CIPHERS),
      server_side=True)

  print 'Will start server at localhost:%d, use <Ctrl-C> to stop.' % PORT
  server.serve_forever()
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#
# serve to show the default.

import sys
import os

#sys.path.append(os.path.abspath('sphinxext'))
#sys.path.append(os.path.abspath('../../'))
#sys.path.append(os.path.abspath('~/anaconda/lib/python2.7/site-packages/'))

# -- General configuration -----------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc',
'sphinx.ext.doctest','sphinx.ext.graphviz', 'sphinx.ext.intersphinx',
'sphinx.ext.autosummary',
'sphinx.ext.pngmath','sphinx.ext.viewcode', 'sphinxcontrib.napoleon']

#doctest extension config values
#doctest_path = '/Users/stephens/code/pysal/doc/source/users/tutorials/'
doctest_test_doctest_blocks = ''
#doctest_global_setup =

# Add any paths that contain templates here, relative to this directory.
#templates_path = ['_templates']

# The suffix of source filenames.
source_suffix = '.rst'
#source_suffix = '.txt'

# The encoding of source files.
#source_encoding = 'utf-8'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = u'pysal'
copyright = u'2014-, PySAL Developers; 2009-13 Sergio Rey'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '1.12.0'
# The full version, including alpha/beta/rc tags.
release = '1.12.0'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of documents that shouldn't be included in the build.
#unused_docs = []

# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = []

# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = False

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# -- Options for HTML output ---------------------------------------------------
#custom strikethrough setup
#html_style = 'mydoc.css'

# The theme to use for HTML and HTML Help pages.  Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
#html_theme = 'default'
#html_theme = 'sphinxdoc'

on_rtd = os.environ.get('READTHEDOCS', None) == 'True'

if not on_rtd:
    import sphinx_rtd_theme
    html_theme = "sphinx_rtd_theme"
    #html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
    html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#html_theme_options = {
#        "rightsidebar": "true",
#        "relbarbgcolor": "CornflowerBlue",
#        "sidebartextcolor": "black",
#        "sidebarlinkcolor": "#355f7c",
#        "sidebarbgcolor": "#F2F2F2",
#        "codebgcolor": "AliceBlue",
#        "footerbgcolor": "Black",
#        "externalrefs": "false",
#        "bodyfont": "Optima",
#        "headfont": "Optima "
#        }

# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = "%s v%s Reference Guide" % (project, version)
html_title = "Python Spatial Analysis Library"

# A shorter title for the navigation bar.  Default is the same as html_title.
html_short_title = "PySAL"

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
html_favicon = 'favicon.png'

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_use_modindex = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''

# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''

# Output file base name for HTML help builder.
htmlhelp_basename = 'pysaldoc'

# -- Options for LaTeX output --------------------------------------------------

# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'

# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
  ('index', 'pysal.tex', u'pysal Documentation',
   u'PySAL Developers', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# Additional stuff for the LaTeX preamble.
#latex_preamble = ''

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_use_modindex = True

# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'http://docs.python.org/': None}

#numpydoc option
numpydoc_show_class_members = True
numpydoc_class_members_toctree = False
<commit_msg>doc: napoleon is in sphinx.ext since 1.3, and adding config parameters<commit_after># -*- coding: utf-8 -*-
#
# serve to show the default.

import sys
import os

#sys.path.append(os.path.abspath('sphinxext'))
#sys.path.append(os.path.abspath('../../'))
#sys.path.append(os.path.abspath('~/anaconda/lib/python2.7/site-packages/'))

# -- General configuration -----------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc',
'sphinx.ext.doctest','sphinx.ext.graphviz', 'sphinx.ext.intersphinx',
'sphinx.ext.autosummary',
'sphinx.ext.pngmath','sphinx.ext.viewcode', 'sphinx.ext.napoleon']

#doctest extension config values
#doctest_path = '/Users/stephens/code/pysal/doc/source/users/tutorials/'
doctest_test_doctest_blocks = ''
#doctest_global_setup =

# Add any paths that contain templates here, relative to this directory.
#templates_path = ['_templates']

# The suffix of source filenames.
source_suffix = '.rst'
#source_suffix = '.txt'

# The encoding of source files.
#source_encoding = 'utf-8'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = u'pysal'
copyright = u'2014-, PySAL Developers; 2009-13 Sergio Rey'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '1.12.0'
# The full version, including alpha/beta/rc tags.
release = '1.12.0'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of documents that shouldn't be included in the build.
#unused_docs = []

# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = []

# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = False

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# -- Options for HTML output ---------------------------------------------------
#custom strikethrough setup
#html_style = 'mydoc.css'

# The theme to use for HTML and HTML Help pages.  Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
#html_theme = 'default'
#html_theme = 'sphinxdoc'

on_rtd = os.environ.get('READTHEDOCS', None) == 'True'

if not on_rtd:
    import sphinx_rtd_theme
    html_theme = "sphinx_rtd_theme"
    #html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
    html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#html_theme_options = {
#        "rightsidebar": "true",
#        "relbarbgcolor": "CornflowerBlue",
#        "sidebartextcolor": "black",
#        "sidebarlinkcolor": "#355f7c",
#        "sidebarbgcolor": "#F2F2F2",
#        "codebgcolor": "AliceBlue",
#        "footerbgcolor": "Black",
#        "externalrefs": "false",
#        "bodyfont": "Optima",
#        "headfont": "Optima "
#        }

# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = "%s v%s Reference Guide" % (project, version)
html_title = "Python Spatial Analysis Library"

# A shorter title for the navigation bar.  Default is the same as html_title.
html_short_title = "PySAL"

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
html_favicon = 'favicon.png'

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_use_modindex = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''

# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''

# Output file base name for HTML help builder.
htmlhelp_basename = 'pysaldoc'

# -- Options for LaTeX output --------------------------------------------------

# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'

# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
  ('index', 'pysal.tex', u'pysal Documentation',
   u'PySAL Developers', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# Additional stuff for the LaTeX preamble.
#latex_preamble = ''

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_use_modindex = True

# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'http://docs.python.org/': None}

#numpydoc option
numpydoc_show_class_members = True
numpydoc_class_members_toctree = False

# Napoleon settings
napoleon_google_docstring = True
napoleon_numpy_docstring = True
napoleon_include_init_with_doc = False
napoleon_include_private_with_doc = False
napoleon_include_special_with_doc = False
napoleon_use_admonition_for_examples = False
napoleon_use_admonition_for_notes = False
napoleon_use_admonition_for_references = False
napoleon_use_ivar = False
napoleon_use_param = True
napoleon_use_rtype = True
napoleon_use_keyword = True
<|endoftext|>


<commit_before># Copyright (c) 2017 Niklas Rosenstein
#
# 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 collections
import contextlib
import itertools
import localimport
import os
import types

from .config import Config
from .manifest import PackageManifest

PPY_MODULES = 'ppy_modules'
PACKAGE_JSON = 'package.json'

class ResolveError(Exception):
  """
  Raised when a module name could not be resolved.
  """

class Session(object):
  """
  A session represents a complete and isolated ppy environment.
  """

  def __init__(self, config=None):
    self.config = config = config or Config()
    self.path = []
    self.main_module = None
    self.module_cache = {}
    self.manifest_cache = {}
    self.current_modules = collections.deque()
    self.preserve_symlinks = config.get('preserve_symlinks') == 'true'

    prefix = config.get('prefix')
    if prefix:
      self.path.append(os.path.join(prefix, PPY_MODULES))
    self.path.extend(filter(bool, config.get('path', '').split(os.pathsep)))
    self.path.extend(filter(bool, os.environ.get('PPY_PATH', '').split(os.pathsep)))

    # For now, standard Python modules are only support from the the local
    # `ppy_modules/.pymodules` directory. In the future, we may add loading
    # Python modules with a separate `localimport` for *every*
    # `ppy_modules/.pymodules` directory.
    self.localimport = localimport.localimport([PPY_MODULES + '/.pymodules'], '.')

  def __enter__(self):
    self.localimport.__enter__()
    return self

  def __exit__(self, *args):
    self.localimport.__exit__(*args)

  @contextlib.contextmanager
  def enter_module(self, module):
    assert isinstance(module, Module)
    self.current_modules.append(module)
    try:
      yield
    finally:
      assert self.current_modules.pop() is module

  def get_manifest(self, filename):
    """
    Parses a #PackageManifest from *filename* and returns it. The manifest
    is cached by the absolute and normalized *filename*.
    """

    filename = os.path.normpath(os.path.abspath(filename))
    if filename in self.manifest_cache:
      return self.manifest_cache[filename]
    manifest = PackageManifest.parse_file(filename)
    self.manifest_cache[filename] = manifest
    return manifest

  def get_module(self, filename):
    """
    Returns a #Module from the specified *filename*. If the module is not
    already cached, it will be created. Note that the module is not necessarily
    loaded. You can do that with #Module.load().
    """

    filename = os.path.normpath(os.path.abspath(filename))
    if filename in self.module_cache:
      return self.module_cache[filename]
    module = Module(filename, self)
    self.module_cache[filename] = module
    return module

  def resolve(self, request, current_dir=None, is_main=False):
    """
    Uses #resolve_module_filename() and raises #ResolveError if the *request*
    could not be resolved. Returns a #Module object. If *is_main* is #True,
    the #Session.main_module will be set. Note that if #Session.main_module
    is already set, a #RuntimeError will be raised if *is_main* is #True.
    """

    if is_main and self.main_module:
      raise RuntimeError('already have a main module')
    current_dir = current_dir or os.getcwd()
    filename = self.resolve_module_filename(request, current_dir, is_main)
    if not filename:
      raise RuntimeError(request, current_dir)
    module = self.get_module(filename)
    if is_main:
      self.main_module = module
    return module

  def resolve_module_filename(self, request, current_dir, is_main):
    """
    Resolves the filename for a Python module from the specified *request*
    name. This may be a relative name like `./path/to/module` in which case
    it is evaluated solely in *current_dir*. Otherwise, if the requested module
    is in standard format (eg. `module-name/core`), the module name is resolved
    in the ppy search path.

    If no file can be found, #None is returned.
    """

    def try_file_(filename):
      return try_file(filename, self.preserve_symlinks and not is_main)

    if os.path.isabs(request):
      main = 'index.py'
      if os.path.isdir(request):
        mffile = os.path.join(request, PACKAGE_JSON)
        if os.path.isfile(mffile):
          # TODO: Only log invalid manifests..?
          main = self.get_manifest(mffile).main
      return try_file_(request) or try_file_(request + '.py') \
          or try_file_(os.path.join(request, main))

    current_dir = current_dir or os.getcwd()
    if ispurerelative(request):
      filename = os.path.normpath(os.path.join(current_dir, request))
      return self.resolve_module_filename(filename, current_dir, is_main)

    for path in itertools.chain(self.iter_module_paths(current_dir, is_main), self.path):
      filename = os.path.normpath(os.path.abspath(os.path.join(path, request)))
      filename = self.resolve_module_filename(filename, current_dir, is_main)
      if filename:
        return filename

    return None

  def iter_module_paths(self, from_dir, is_main=False):
    """
    Yield all possible `ppy_modules/` paths that can be matched starting from
    *from_dir*. Note that the method can yield directories that don't exist.
    """

    if is_main:
      yield '.'
    from_dir = os.path.normpath(os.path.abspath(from_dir))
    while from_dir:
      dirname, base = os.path.split(from_dir)
      if base != PPY_MODULES:
        # Avoid yielding paths like `ppy_modules/ppy_modules`.
        yield os.path.join(from_dir, 'ppy_modules')
      if from_dir == dirname:
        # If we hit a drive letter on Windows.
        break
      from_dir = dirname

    if os.name != 'nt':
      # TODO: Node does this, but I'm not sure if we need that with our
      # current implementation of this method.
      yield '/node_modules'

class Module(object):
  """
  Represents a ppy Python module.
  """

  def __init__(self, filename, session):
    self.filename = filename
    self.directory = os.path.dirname(filename)
    self.namespace = types.ModuleType(filename)
    self.session = session
    self.loaded = False

    # Initialize the module's namespace.
    vars(self.namespace).update({
      '_filename': filename,
      '_dirname': os.path.dirname(filename),
      'require': Require(self)
    })

  def __repr__(self):
    return '<Module {!r}>'.format(self.filename)

  def load(self):
    if self.loaded:
      raise RuntimeError('module already loaded')
    self.loaded = True
    with self.session.enter_module(self):
      with open(self.filename, 'r') as fp:
        code = fp.read()
      code = compile(code, self.filename, 'exec')
      exec(code, vars(self.namespace))

class Require(object):
  """
  Implements the `require()` function. An object of this class is created
  separately for every #Module.
  """

  ResolveError = ResolveError

  def __init__(self, module):
    self.module = module
    self.session = module.session

  def __repr__(self):
    return '<Require of {!r}>'.format(self.module)

  def __call__(self, name):
    filename = self.session.resolve_module_filename(name, self.module.directory, False)
    if not filename:
      raise ResolveError(name, self.module.directory)
    module = self.session.get_module(filename)
    if not module.loaded:
      module.load()
    return module

def try_file(filename, preserve_symlinks, is_main=False):
  """
  Returns *filename* if it exists. If *is_main* is #False and
  `--preserve-symlinks`, symlinks will be kept intact.
  """

  if os.path.isfile(filename):
    if not preserve_symlinks and not is_main and os.path.islink(filename):
      os.path.relpath(filename)
    return filename
  return None

def ispurerelative(path):
  """
  Returns #True if *path* is a purely relative filename, that is if it begins
  with `./` or `../`. Returns #False otherwise.
  """

  return path.startswith('./') or path.startswith('../')
<commit_msg>add Require.is_main, Require.main, Require.__call__(get_exports), __call__() now returns exports or namespace instead of module object<commit_after># Copyright (c) 2017 Niklas Rosenstein
#
# 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 collections
import contextlib
import itertools
import localimport
import os
import types

from .config import Config
from .manifest import PackageManifest

PPY_MODULES = 'ppy_modules'
PACKAGE_JSON = 'package.json'

class ResolveError(Exception):
  """
  Raised when a module name could not be resolved.
  """

class Session(object):
  """
  A session represents a complete and isolated ppy environment.
  """

  def __init__(self, config=None):
    self.config = config = config or Config()
    self.path = []
    self.main_module = None
    self.module_cache = {}
    self.manifest_cache = {}
    self.current_modules = collections.deque()
    self.preserve_symlinks = config.get('preserve_symlinks') == 'true'

    prefix = config.get('prefix')
    if prefix:
      self.path.append(os.path.join(prefix, PPY_MODULES))
    self.path.extend(filter(bool, config.get('path', '').split(os.pathsep)))
    self.path.extend(filter(bool, os.environ.get('PPY_PATH', '').split(os.pathsep)))

    # For now, standard Python modules are only support from the the local
    # `ppy_modules/.pymodules` directory. In the future, we may add loading
    # Python modules with a separate `localimport` for *every*
    # `ppy_modules/.pymodules` directory.
    self.localimport = localimport.localimport([PPY_MODULES + '/.pymodules'], '.')

  def __enter__(self):
    self.localimport.__enter__()
    return self

  def __exit__(self, *args):
    self.localimport.__exit__(*args)

  @contextlib.contextmanager
  def enter_module(self, module):
    assert isinstance(module, Module)
    self.current_modules.append(module)
    try:
      yield
    finally:
      assert self.current_modules.pop() is module

  def get_manifest(self, filename):
    """
    Parses a #PackageManifest from *filename* and returns it. The manifest
    is cached by the absolute and normalized *filename*.
    """

    filename = os.path.normpath(os.path.abspath(filename))
    if filename in self.manifest_cache:
      return self.manifest_cache[filename]
    manifest = PackageManifest.parse_file(filename)
    self.manifest_cache[filename] = manifest
    return manifest

  def get_module(self, filename):
    """
    Returns a #Module from the specified *filename*. If the module is not
    already cached, it will be created. Note that the module is not necessarily
    loaded. You can do that with #Module.load().
    """

    filename = os.path.normpath(os.path.abspath(filename))
    if filename in self.module_cache:
      return self.module_cache[filename]
    module = Module(filename, self)
    self.module_cache[filename] = module
    return module

  def resolve(self, request, current_dir=None, is_main=False):
    """
    Uses #resolve_module_filename() and raises #ResolveError if the *request*
    could not be resolved. Returns a #Module object. If *is_main* is #True,
    the #Session.main_module will be set. Note that if #Session.main_module
    is already set, a #RuntimeError will be raised if *is_main* is #True.
    """

    if is_main and self.main_module:
      raise RuntimeError('already have a main module')
    current_dir = current_dir or os.getcwd()
    filename = self.resolve_module_filename(request, current_dir, is_main)
    if not filename:
      raise RuntimeError(request, current_dir)
    module = self.get_module(filename)
    if is_main:
      self.main_module = module
    return module

  def resolve_module_filename(self, request, current_dir, is_main):
    """
    Resolves the filename for a Python module from the specified *request*
    name. This may be a relative name like `./path/to/module` in which case
    it is evaluated solely in *current_dir*. Otherwise, if the requested module
    is in standard format (eg. `module-name/core`), the module name is resolved
    in the ppy search path.

    If no file can be found, #None is returned.
    """

    def try_file_(filename):
      return try_file(filename, self.preserve_symlinks and not is_main)

    if os.path.isabs(request):
      main = 'index.py'
      if os.path.isdir(request):
        mffile = os.path.join(request, PACKAGE_JSON)
        if os.path.isfile(mffile):
          # TODO: Only log invalid manifests..?
          main = self.get_manifest(mffile).main
      return try_file_(request) or try_file_(request + '.py') \
          or try_file_(os.path.join(request, main))

    current_dir = current_dir or os.getcwd()
    if ispurerelative(request):
      filename = os.path.normpath(os.path.join(current_dir, request))
      return self.resolve_module_filename(filename, current_dir, is_main)

    for path in itertools.chain(self.iter_module_paths(current_dir, is_main), self.path):
      filename = os.path.normpath(os.path.abspath(os.path.join(path, request)))
      filename = self.resolve_module_filename(filename, current_dir, is_main)
      if filename:
        return filename

    return None

  def iter_module_paths(self, from_dir, is_main=False):
    """
    Yield all possible `ppy_modules/` paths that can be matched starting from
    *from_dir*. Note that the method can yield directories that don't exist.
    """

    if is_main:
      yield '.'
    from_dir = os.path.normpath(os.path.abspath(from_dir))
    while from_dir:
      dirname, base = os.path.split(from_dir)
      if base != PPY_MODULES:
        # Avoid yielding paths like `ppy_modules/ppy_modules`.
        yield os.path.join(from_dir, 'ppy_modules')
      if from_dir == dirname:
        # If we hit a drive letter on Windows.
        break
      from_dir = dirname

    if os.name != 'nt':
      # TODO: Node does this, but I'm not sure if we need that with our
      # current implementation of this method.
      yield '/node_modules'

class Module(object):
  """
  Represents a ppy Python module.
  """

  def __init__(self, filename, session):
    self.filename = filename
    self.directory = os.path.dirname(filename)
    self.namespace = types.ModuleType(filename)
    self.session = session
    self.loaded = False

    # Initialize the module's namespace.
    vars(self.namespace).update({
      '_filename': filename,
      '_dirname': os.path.dirname(filename),
      'require': Require(self)
    })

  def __repr__(self):
    return '<Module {!r}>'.format(self.filename)

  def load(self):
    if self.loaded:
      raise RuntimeError('module already loaded')
    self.loaded = True
    with self.session.enter_module(self):
      with open(self.filename, 'r') as fp:
        code = fp.read()
      code = compile(code, self.filename, 'exec')
      exec(code, vars(self.namespace))

class Require(object):
  """
  Implements the `require()` function. An object of this class is created
  separately for every #Module.
  """

  ResolveError = ResolveError

  def __init__(self, module):
    self.module = module
    self.session = module.session

  def __repr__(self):
    return '<Require of {!r}>'.format(self.module)

  def __call__(self, name, get_exports=True):
    module = self.session.get_module(self.resolve(name))
    if not module.loaded:
      module.load()
    result = module.namespace
    if get_exports and hasattr(result, 'exports'):
      result = result.exports
    return result

  def resolve(self, name):
    filename = self.session.resolve_module_filename(name, self.module.directory, False)
    if not filename:
      raise ResolveError(name, self.module.directory)
    return filename

  @property
  def is_main(self):
    return self.module is self.main

  @property
  def main(self):
    return self.session.main_module

def try_file(filename, preserve_symlinks, is_main=False):
  """
  Returns *filename* if it exists. If *is_main* is #False and
  `--preserve-symlinks`, symlinks will be kept intact.
  """

  if os.path.isfile(filename):
    if not preserve_symlinks and not is_main and os.path.islink(filename):
      os.path.relpath(filename)
    return filename
  return None

def ispurerelative(path):
  """
  Returns #True if *path* is a purely relative filename, that is if it begins
  with `./` or `../`. Returns #False otherwise.
  """

  return path.startswith('./') or path.startswith('../')
<|endoftext|>


<commit_before>#!/usr/bin/env python3
from bs4 import BeautifulSoup
import PIL.Image
import json
import io
import posixpath
import re
import sys
import threading
import time
import urllib.parse
import xml.etree.ElementTree as ET

import parallel
import web_cache
import generate_pages

_page_cache = {}
_pil_image_cache = {}

def _page(url):
    global _page_cache
    if url not in _page_cache:
        _page_cache[url] = BeautifulSoup(web_cache.get(url), "lxml")
    return _page_cache[url]

def _pil_image(url):
    global _pil_image_cache
    if url not in _pil_image_cache:
        _pil_image_cache[url] = PIL.Image.open(io.BytesIO(web_cache.get(url)))
    return _pil_image_cache[url]

def _fetch_year_month_queries():
    for year in range(2006, 2018):
        web_cache.get("https://thearchdruidreport.blogspot.com/%04d/" % year)
        for month in range(1, 13):
            web_cache.get("https://thearchdruidreport.blogspot.com/%04d/%02d/" % (year, month))

def _fetch_stylesheet_resources(css, base_url):
    i = 0
    p1 = re.compile(r"url\(")
    p2 = re.compile(r"url\(\"([^\%()\\\"\']+)\"\)")
    p3 = re.compile(r"url\(\'([^\%()\\\"\']+)\'\)")
    p4 = re.compile(r"url\(([^\%()\\\"\']+)\)")
    while True:
        s = p1.search(css, i)
        if s is None:
            break
        i = s.start()
        m = p2.match(css, i) or p3.match(css, i) or p4.match(css, i)
        url = m.group(1)
        if url.startswith("data:"):
            pass
        else:
            url = urllib.parse.urljoin(base_url, url)
            web_cache.get(url)
        i = m.end()

def _fetch_page_resources(url):
    doc = _page(url)
    for img in doc.find_all("img"):
        assert "delayLoad" not in img.attrs.get("class", [])
        assert "longdesc" not in img.attrs
        try:
            web_cache.get(urllib.parse.urljoin(url, img.attrs["src"]))
        except web_cache.ResourceNotAvailable:
            pass

        # As in generate_pages.py, if we have an image linking to a another
        # (i.e. bigger) Blogspot image, cache the linked image.  (This step
        # doesn't seem to do anything, because generate_pages.py already cached
        # everything.)
        if img.parent.name == "a" and "href" in img.parent.attrs:
            href = img.parent.attrs["href"]
            if re.match(r"(https?:)?//[1234]\.bp\.blogspot\.com/.*\.(png|gif|jpg|jpeg)$", href, re.IGNORECASE):
                web_cache.get(href)

    for link in doc.find_all("link"):
        rel = link.attrs["rel"]
        href = link.attrs["href"]
        href = urllib.parse.urljoin(url, href)
        if rel == "stylesheet":
            _fetch_stylesheet_resources(web_cache.get(href).decode("utf8"), href)
        if link.attrs["rel"] == "icon":
            web_cache.get(href)
    for script in doc.find_all("script"):
        if "src" in script.attrs:
            web_cache.get(urllib.parse.urljoin(url, script.attrs["src"]))
    for style in doc.find_all("style"):
        assert len(style.contents) == 1
        assert style.contents[0].name is None
        _fetch_stylesheet_resources(str(style.contents[0].string), url)

def _crawl_mobile_post_listings(apply_, flush):
    print("Crawling mobile post listings...")
    url = "https://thearchdruidreport.blogspot.com/?m=1"
    while url is not None:
        doc = _page(url)
        apply_(_fetch_page_resources, (url,))
        next_anchor = doc.select("a.blog-pager-older-link")
        if len(next_anchor) == 0:
            url = None
        else:
            (next_anchor,) = next_anchor
            url = next_anchor.attrs["href"]
    flush()

def _crawl_mobile_post(url):
    print("Crawling %s ..." % url)
    sys.stdout.flush()
    _fetch_page_resources(url)

def _crawl_mobile_posts(apply_, flush):
    for p in generate_pages.load_posts():
        apply_(_crawl_mobile_post, (p.url + "?m=1",))

def _download_comments_feed(url):
    print("Crawling %s Atom comments feed..." % url)
    doc = _page(url + "?m=1")
    post_id = doc.find("meta", itemprop="postId").attrs["content"]
    atom_feed = "https://thearchdruidreport.blogspot.com/feeds/%s/comments/default" % post_id
    # Get both the JSON version and the Atom XML version.  Make sure they parse.
    ET.fromstring(  web_cache.get(atom_feed + "?alt=atom&v=2&orderby=published&reverse=false&max-results=1000"))
    js = json.loads(web_cache.get(atom_feed + "?alt=json&v=2&orderby=published&reverse=false&max-results=1000").decode("utf8"))
    # These avatar images are special, because they're full-size.  Images
    # sourced from Blogger's HTML files are usually (but not always!) shrunk
    # to under 35x35.  Maybe it'd be nice to downlod these full-size images and
    # provide sharper avatars for retina displays, but I think it'd make the
    # web_cache much bigger.  Leave them out, at least for now.
    if False:
        for comment in js["feed"]["entry"]:
            (author,) = comment["author"]
            avatar = author["gd$image"]
            int(avatar["width"])
            int(avatar["height"])
            try:
                img = _pil_image(avatar["src"])
            except:
                print("WARNING: Bad avatar URL: %s" % avatar["src"])

def _download_comments_feeds(apply_):
    for p in generate_pages.load_posts():
        apply_(_download_comments_feed, (p.url,))

def _download_posts_bare():
    count = 0
    for start in range(1, 600, 100):
        ET.fromstring(  web_cache.get("https://thearchdruidreport.blogspot.com/feeds/posts/default?alt=atom&start-index=%d&max-results=100" % start).decode("utf8"))
        js = json.loads(web_cache.get("https://thearchdruidreport.blogspot.com/feeds/posts/default?alt=json&start-index=%d&max-results=100" % start).decode("utf8"))
        for entry in js["feed"]["entry"]:
            count += 1
    assert count == len(generate_pages.load_posts())

def _main(apply_, flush):
    for url in [
            # There are two desktop pages for viewing comments -- the main
            # read-only page and a special white-backgrounded page for entering
            # and viewing comments.  For 2013/01/into-unknown-country.html,
            # this is the URL for the latter (two pages):
            "https://www.blogger.com/comment.g?blogID=27481991&postID=5625187186942053195",
            "https://www.blogger.com/comment.g?postID=5625187186942053195&blogID=27481991&isPopup=false&page=2",
            # This is a comments page where there is a reply:
            #  - desktop URL: http://thearchdruidreport.blogspot.com/2017/03/the-magic-lantern-show.html
            "https://www.blogger.com/comment.g?blogID=27481991&postID=1891285484434881454",
        ]:
        _fetch_page_resources(url)

    _fetch_year_month_queries()
    _crawl_mobile_post_listings(apply_, flush)
    _crawl_mobile_posts(apply_, flush)
    _download_comments_feeds(apply_)
    _download_posts_bare()

if __name__ == "__main__":
    parallel.run_main(_main)

# Use something like this on mobile to request comments.
# x = requests.get('https://thearchdruidreport.blogspot.com/feeds/739164683723753251/comments/default?alt=json&v=2&orderby=published&reverse=false&max-results=1000').json

# This page has a reply, 125 comments
#  - http://thearchdruidreport.blogspot.com/2017/03/the-magic-lantern-show.html?m=1
# This comment is a reply, but you can't tell on the desktop site:
#  - http://thearchdruidreport.blogspot.com/2017/03/the-magic-lantern-show.html?showComment=1488647590675&m=1#c3012836938846340532
#  - http://thearchdruidreport.blogspot.com/2017/03/the-magic-lantern-show.html?showComment=1488647590675#c3012836938846340532
# Links for this page:
#   <link rel="alternate" type="application/atom+xml" title="The Archdruid Report - Atom" href="http://thearchdruidreport.blogspot.com/feeds/posts/default" />
#   <link rel="alternate" type="application/rss+xml" title="The Archdruid Report - RSS" href="http://thearchdruidreport.blogspot.com/feeds/posts/default?alt=rss" />
#   <link rel="service.post" type="application/atom+xml" title="The Archdruid Report - Atom" href="https://www.blogger.com/feeds/27481991/posts/default" />
#   <link rel="alternate" type="application/atom+xml" title="The Archdruid Report - Atom" href="http://thearchdruidreport.blogspot.com/feeds/1891285484434881454/comments/default" />
<commit_msg>Flush at various points for better log output<commit_after>#!/usr/bin/env python3
from bs4 import BeautifulSoup
import PIL.Image
import json
import io
import posixpath
import re
import sys
import threading
import time
import urllib.parse
import xml.etree.ElementTree as ET

import parallel
import web_cache
import generate_pages

_page_cache = {}
_pil_image_cache = {}

def _page(url):
    global _page_cache
    if url not in _page_cache:
        _page_cache[url] = BeautifulSoup(web_cache.get(url), "lxml")
    return _page_cache[url]

def _pil_image(url):
    global _pil_image_cache
    if url not in _pil_image_cache:
        _pil_image_cache[url] = PIL.Image.open(io.BytesIO(web_cache.get(url)))
    return _pil_image_cache[url]

def _fetch_year_month_queries():
    for year in range(2006, 2018):
        web_cache.get("https://thearchdruidreport.blogspot.com/%04d/" % year)
        for month in range(1, 13):
            web_cache.get("https://thearchdruidreport.blogspot.com/%04d/%02d/" % (year, month))

def _fetch_stylesheet_resources(css, base_url):
    i = 0
    p1 = re.compile(r"url\(")
    p2 = re.compile(r"url\(\"([^\%()\\\"\']+)\"\)")
    p3 = re.compile(r"url\(\'([^\%()\\\"\']+)\'\)")
    p4 = re.compile(r"url\(([^\%()\\\"\']+)\)")
    while True:
        s = p1.search(css, i)
        if s is None:
            break
        i = s.start()
        m = p2.match(css, i) or p3.match(css, i) or p4.match(css, i)
        url = m.group(1)
        if url.startswith("data:"):
            pass
        else:
            url = urllib.parse.urljoin(base_url, url)
            web_cache.get(url)
        i = m.end()

def _fetch_page_resources(url):
    doc = _page(url)
    for img in doc.find_all("img"):
        assert "delayLoad" not in img.attrs.get("class", [])
        assert "longdesc" not in img.attrs
        try:
            web_cache.get(urllib.parse.urljoin(url, img.attrs["src"]))
        except web_cache.ResourceNotAvailable:
            pass

        # As in generate_pages.py, if we have an image linking to a another
        # (i.e. bigger) Blogspot image, cache the linked image.  (This step
        # doesn't seem to do anything, because generate_pages.py already cached
        # everything.)
        if img.parent.name == "a" and "href" in img.parent.attrs:
            href = img.parent.attrs["href"]
            if re.match(r"(https?:)?//[1234]\.bp\.blogspot\.com/.*\.(png|gif|jpg|jpeg)$", href, re.IGNORECASE):
                web_cache.get(href)

    for link in doc.find_all("link"):
        rel = link.attrs["rel"]
        href = link.attrs["href"]
        href = urllib.parse.urljoin(url, href)
        if rel == "stylesheet":
            _fetch_stylesheet_resources(web_cache.get(href).decode("utf8"), href)
        if link.attrs["rel"] == "icon":
            web_cache.get(href)
    for script in doc.find_all("script"):
        if "src" in script.attrs:
            web_cache.get(urllib.parse.urljoin(url, script.attrs["src"]))
    for style in doc.find_all("style"):
        assert len(style.contents) == 1
        assert style.contents[0].name is None
        _fetch_stylesheet_resources(str(style.contents[0].string), url)

def _crawl_mobile_post_listings(apply_, flush):
    print("Crawling mobile post listings...")
    sys.stdout.flush()
    url = "https://thearchdruidreport.blogspot.com/?m=1"
    while url is not None:
        doc = _page(url)
        apply_(_fetch_page_resources, (url,))
        next_anchor = doc.select("a.blog-pager-older-link")
        if len(next_anchor) == 0:
            url = None
        else:
            (next_anchor,) = next_anchor
            url = next_anchor.attrs["href"]
    flush()

def _crawl_mobile_post(url):
    print("Crawling %s ..." % url)
    sys.stdout.flush()
    _fetch_page_resources(url)

def _crawl_mobile_posts(apply_, flush):
    for p in generate_pages.load_posts():
        apply_(_crawl_mobile_post, (p.url + "?m=1",))
    flush()

def _download_comments_feed(url):
    print("Crawling %s Atom comments feed..." % url)
    doc = _page(url + "?m=1")
    post_id = doc.find("meta", itemprop="postId").attrs["content"]
    atom_feed = "https://thearchdruidreport.blogspot.com/feeds/%s/comments/default" % post_id
    # Get both the JSON version and the Atom XML version.  Make sure they parse.
    ET.fromstring(  web_cache.get(atom_feed + "?alt=atom&v=2&orderby=published&reverse=false&max-results=1000"))
    js = json.loads(web_cache.get(atom_feed + "?alt=json&v=2&orderby=published&reverse=false&max-results=1000").decode("utf8"))
    # These avatar images are special, because they're full-size.  Images
    # sourced from Blogger's HTML files are usually (but not always!) shrunk
    # to under 35x35.  Maybe it'd be nice to downlod these full-size images and
    # provide sharper avatars for retina displays, but I think it'd make the
    # web_cache much bigger.  Leave them out, at least for now.
    if False:
        for comment in js["feed"]["entry"]:
            (author,) = comment["author"]
            avatar = author["gd$image"]
            int(avatar["width"])
            int(avatar["height"])
            try:
                img = _pil_image(avatar["src"])
            except:
                print("WARNING: Bad avatar URL: %s" % avatar["src"])

def _download_comments_feeds(apply_, flush):
    for p in generate_pages.load_posts():
        apply_(_download_comments_feed, (p.url,))
    flush()

def _download_posts_bare():
    count = 0
    for start in range(1, 600, 100):
        ET.fromstring(  web_cache.get("https://thearchdruidreport.blogspot.com/feeds/posts/default?alt=atom&start-index=%d&max-results=100" % start).decode("utf8"))
        js = json.loads(web_cache.get("https://thearchdruidreport.blogspot.com/feeds/posts/default?alt=json&start-index=%d&max-results=100" % start).decode("utf8"))
        for entry in js["feed"]["entry"]:
            count += 1
    assert count == len(generate_pages.load_posts())

def _main(apply_, flush):
    for url in [
            # There are two desktop pages for viewing comments -- the main
            # read-only page and a special white-backgrounded page for entering
            # and viewing comments.  For 2013/01/into-unknown-country.html,
            # this is the URL for the latter (two pages):
            "https://www.blogger.com/comment.g?blogID=27481991&postID=5625187186942053195",
            "https://www.blogger.com/comment.g?postID=5625187186942053195&blogID=27481991&isPopup=false&page=2",
            # This is a comments page where there is a reply:
            #  - desktop URL: http://thearchdruidreport.blogspot.com/2017/03/the-magic-lantern-show.html
            "https://www.blogger.com/comment.g?blogID=27481991&postID=1891285484434881454",
        ]:
        _fetch_page_resources(url)

    _fetch_year_month_queries()
    _crawl_mobile_post_listings(apply_, flush)
    _crawl_mobile_posts(apply_, flush)
    _download_comments_feeds(apply_, flush)
    _download_posts_bare()

if __name__ == "__main__":
    parallel.run_main(_main)

# Use something like this on mobile to request comments.
# x = requests.get('https://thearchdruidreport.blogspot.com/feeds/739164683723753251/comments/default?alt=json&v=2&orderby=published&reverse=false&max-results=1000').json

# This page has a reply, 125 comments
#  - http://thearchdruidreport.blogspot.com/2017/03/the-magic-lantern-show.html?m=1
# This comment is a reply, but you can't tell on the desktop site:
#  - http://thearchdruidreport.blogspot.com/2017/03/the-magic-lantern-show.html?showComment=1488647590675&m=1#c3012836938846340532
#  - http://thearchdruidreport.blogspot.com/2017/03/the-magic-lantern-show.html?showComment=1488647590675#c3012836938846340532
# Links for this page:
#   <link rel="alternate" type="application/atom+xml" title="The Archdruid Report - Atom" href="http://thearchdruidreport.blogspot.com/feeds/posts/default" />
#   <link rel="alternate" type="application/rss+xml" title="The Archdruid Report - RSS" href="http://thearchdruidreport.blogspot.com/feeds/posts/default?alt=rss" />
#   <link rel="service.post" type="application/atom+xml" title="The Archdruid Report - Atom" href="https://www.blogger.com/feeds/27481991/posts/default" />
#   <link rel="alternate" type="application/atom+xml" title="The Archdruid Report - Atom" href="http://thearchdruidreport.blogspot.com/feeds/1891285484434881454/comments/default" />
<|endoftext|>


<commit_before>"""
Django settings for COMP204P project.

Generated by 'django-admin startproject' using Django 1.8.5.

For more information on this file, see
https://docs.djangoproject.com/en/1.8/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.8/ref/settings/
"""

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

from passwords import GITHUB_SECRET_KEY
SECRET_KEY = GITHUB_SECRET_KEY

DEBUG = False

ALLOWED_HOSTS = ['127.0.0.1', 'localhost', '.ucl.ac.uk']

# Application definition

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'corsheaders',
    'ToDoList',
)

LOGIN_URL = '/login/'

LOGIN_EXEMPT_URLS = (
 r'^admin/',
)

CORS_ORIGIN_WHITELIST = (
    'github.com',
)

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.middleware.security.SecurityMiddleware',
)

ROOT_URLCONF = 'COMP204P.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': ['COMP204P/templates'],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'COMP204P.wsgi.application'

# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/

STATIC_URL = '/static/'
<commit_msg>fix allowed_hosts<commit_after>"""
Django settings for COMP204P project.

Generated by 'django-admin startproject' using Django 1.8.5.

For more information on this file, see
https://docs.djangoproject.com/en/1.8/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.8/ref/settings/
"""

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

from passwords import GITHUB_SECRET_KEY
SECRET_KEY = GITHUB_SECRET_KEY

DEBUG = False

ALLOWED_HOSTS = ['localhost', '.ucl.ac.uk']

# Application definition

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'corsheaders',
    'ToDoList',
)

LOGIN_URL = '/login/'

LOGIN_EXEMPT_URLS = (
 r'^admin/',
)

CORS_ORIGIN_WHITELIST = (
    'github.com',
)

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.middleware.security.SecurityMiddleware',
)

ROOT_URLCONF = 'COMP204P.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': ['COMP204P/templates'],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'COMP204P.wsgi.application'

# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/

STATIC_URL = '/static/'
<|endoftext|>


<commit_before>#!/usr/bin/python
# @file command_parser.py
# @author mconway@Espial.com
# @description Parse the command issued to run mcmock
# Copyright (C) Espial Limited 2017 Company Confidential - All Rights Reserved

import sys
import re
from os import path, getcwd

help_string = \
"""mcmock Version 1.0
Tool for auto-generating mock files for C headers

EXAMPLES:
    generate_mock.py header_to_mock.h
    generate_mock.py -o /tmp/mocks/ -r /usr/include -m header_one.h header_two.h
    generate_mock.py -o /tmp/mocks/ -r /usr/include -i /usr/custom_include -m header_one.h header_two.h

OPTIONS:
    -h  display mcmock help
    -o  path where to put generated mock files (if not supplied, cwd will be used)
    -r  path to the root directory where include files live
    -i  (space separated) list of additional include directories
    -m  (space separated) list of header files to mock
"""

arg_options = [ '-o', '-m', '-r', '-i' ]

class ParseCommand:

    def get_command_data( self ):
        return self.command_data

    def get_command_errors( self ):
        return self.command_data['errors']

    def show_help_message( self ):
        return self.command_data['show_help']

    def get_help_message( self ):
        return help_string

    def get_headers_to_mock( self ):
        return self.command_data['headers_to_mock']

    def get_root_include_directory( self ):
        return self.command_data['root_include_directory']

    def get_output_directory( self ):
        return self.command_data['output_directory']

    def get_additional_includes( self ):
        return self.command_data['additional_includes']

    def __init__( self, command_args ):
        self.command_data = {}
        self.command_data['headers_to_mock'] = []
        self.command_data['additional_includes'] = []
        self.command_data['root_include_directory'] = ''
        self.command_data['output_directory'] = getcwd()
        self.command_data['errors'] = ''
        self.command_data['show_help'] = False
        if self.__check_command_length( command_args ):
            self.command_data['errors'] = self.__parse_command( command_args )

    def __check_command_length( self, command_args ):
        result = False
        if ( len( command_args ) <= 1 ):
            self.command_data['errors'] = "ERROR: No header files supplied; mcmock can't mock thin air.\nUse -h for more help"
        else:
            if ( len( command_args ) == 2 and ( command_args[1] == '-h' or command_args[1] == '--help') ):
                self.command_data['show_help'] = True
            else:
                result = True
        return result

    def __parse_command( self, command_args ):
        errors = ''
        i = 1
        while ( i < len( command_args ) and not errors):
            arg = command_args[i]
            if ( arg == '-o' ):
                if ( len( command_args ) >= i + 1 ):
                    output_directory = command_args[i+1]
                    if ( not path.exists( output_directory ) or not path.isdir( output_directory ) ):
                        errors = "ERROR: Output directory %s does not exist. Please create it before running this mcmock."%( output_directory )
                    else:
                        if ( output_directory[len(output_directory)-1] != '/' ):
                            output_directory += '/'
                    self.command_data['output_directory'] = output_directory
                else:
                    errors = "ERROR: found -o option with no output directory specified\nTry -h for usage"
                i+=2
            elif ( arg == '-m' ):
                if ( len( command_args ) >= i + 1 ):
                    j = i + 1
                    while ( j < len( command_args ) and not errors ):
                        arg = command_args[j]
                        if arg in arg_options:
                            break;
                        matched = re.match( r'([\/A-Za-z0-9_-]+)(.h)', arg )
                        if matched:
                            self.command_data['headers_to_mock'].append( arg )
                        else:
                            errors = "ERROR: Expected C header file, but got [%s]\nTry -h for usage"%( arg )
                        j+=1
                    i=j
                else:
                    errors = "ERROR: found -m option with no files to mock specified\nTry -h for usage"
            elif ( arg == '-r' ):
                if ( len( command_args ) >= i + 1 ):
                    root_include_directory = command_args[i+1]
                    if ( not path.exists( root_include_directory ) or not path.isdir( root_include_directory ) ):
                        errors = "ERROR: The includes root directory %s does not exist."%( root_include_directory )
                    else:
                        if ( root_include_directory[len( root_include_directory ) - 1] != '/' ):
                            root_include_directory += '/'
                    self.command_data['root_include_directory'] = root_include_directory
                else:
                    errors = "ERROR: found -r option with no root include directory specified\nTry -h for usage"
                i+=2
            elif ( arg == '-i' ):
                if ( len( command_args ) > i + 1 ):
                    j = i + 1
                    while ( j < len( command_args ) and not errors ):
                        arg = command_args[j]
                        if arg in arg_options:
                            break;
                        if ( not path.exists( arg ) or not path.isdir( arg ) ):
                            errors = "ERROR: The additional include directory %s does not exist."%( arg )
                        else:
                            if ( arg[len(arg)-1] != '/' ):
                                arg += '/'
                        self.command_data['additional_includes'].append( command_args[j] )
                        j+=1
                    i=j
                else:
                    errors = "ERROR: found -i option with no additional includes specified\nTry -h for usage"
            else:
                errors = "ERROR: Unknown arg %s\nTry -h for usage"%(arg)
        if not errors and not self.command_data['headers_to_mock']:
            errors = "ERROR: No header(s) to be mocked were specified\nTry -h for usage"
        return errors

<commit_msg>Update Author<commit_after>#!/usr/bin/python
# @file command_parser.py
# @author matthew.denis.conway@gmail.com
# @description Parse the command issued to run mcmock

import sys
import re
from os import path, getcwd

help_string = \
"""mcmock Version 1.0
Tool for auto-generating mock files for C headers

EXAMPLES:
    generate_mock.py header_to_mock.h
    generate_mock.py -o /tmp/mocks/ -r /usr/include -m header_one.h header_two.h
    generate_mock.py -o /tmp/mocks/ -r /usr/include -i /usr/custom_include -m header_one.h header_two.h

OPTIONS:
    -h  display mcmock help
    -o  path where to put generated mock files (if not supplied, cwd will be used)
    -r  path to the root directory where include files live
    -i  (space separated) list of additional include directories
    -m  (space separated) list of header files to mock
"""

arg_options = [ '-o', '-m', '-r', '-i' ]

class ParseCommand:

    def get_command_data( self ):
        return self.command_data

    def get_command_errors( self ):
        return self.command_data['errors']

    def show_help_message( self ):
        return self.command_data['show_help']

    def get_help_message( self ):
        return help_string

    def get_headers_to_mock( self ):
        return self.command_data['headers_to_mock']

    def get_root_include_directory( self ):
        return self.command_data['root_include_directory']

    def get_output_directory( self ):
        return self.command_data['output_directory']

    def get_additional_includes( self ):
        return self.command_data['additional_includes']

    def __init__( self, command_args ):
        self.command_data = {}
        self.command_data['headers_to_mock'] = []
        self.command_data['additional_includes'] = []
        self.command_data['root_include_directory'] = ''
        self.command_data['output_directory'] = getcwd()
        self.command_data['errors'] = ''
        self.command_data['show_help'] = False
        if self.__check_command_length( command_args ):
            self.command_data['errors'] = self.__parse_command( command_args )

    def __check_command_length( self, command_args ):
        result = False
        if ( len( command_args ) <= 1 ):
            self.command_data['errors'] = "ERROR: No header files supplied; mcmock can't mock thin air.\nUse -h for more help"
        else:
            if ( len( command_args ) == 2 and ( command_args[1] == '-h' or command_args[1] == '--help') ):
                self.command_data['show_help'] = True
            else:
                result = True
        return result

    def __parse_command( self, command_args ):
        errors = ''
        i = 1
        while ( i < len( command_args ) and not errors):
            arg = command_args[i]
            if ( arg == '-o' ):
                if ( len( command_args ) >= i + 1 ):
                    output_directory = command_args[i+1]
                    if ( not path.exists( output_directory ) or not path.isdir( output_directory ) ):
                        errors = "ERROR: Output directory %s does not exist. Please create it before running this mcmock."%( output_directory )
                    else:
                        if ( output_directory[len(output_directory)-1] != '/' ):
                            output_directory += '/'
                    self.command_data['output_directory'] = output_directory
                else:
                    errors = "ERROR: found -o option with no output directory specified\nTry -h for usage"
                i+=2
            elif ( arg == '-m' ):
                if ( len( command_args ) >= i + 1 ):
                    j = i + 1
                    while ( j < len( command_args ) and not errors ):
                        arg = command_args[j]
                        if arg in arg_options:
                            break;
                        matched = re.match( r'([\/A-Za-z0-9_-]+)(.h)', arg )
                        if matched:
                            self.command_data['headers_to_mock'].append( arg )
                        else:
                            errors = "ERROR: Expected C header file, but got [%s]\nTry -h for usage"%( arg )
                        j+=1
                    i=j
                else:
                    errors = "ERROR: found -m option with no files to mock specified\nTry -h for usage"
            elif ( arg == '-r' ):
                if ( len( command_args ) >= i + 1 ):
                    root_include_directory = command_args[i+1]
                    if ( not path.exists( root_include_directory ) or not path.isdir( root_include_directory ) ):
                        errors = "ERROR: The includes root directory %s does not exist."%( root_include_directory )
                    else:
                        if ( root_include_directory[len( root_include_directory ) - 1] != '/' ):
                            root_include_directory += '/'
                    self.command_data['root_include_directory'] = root_include_directory
                else:
                    errors = "ERROR: found -r option with no root include directory specified\nTry -h for usage"
                i+=2
            elif ( arg == '-i' ):
                if ( len( command_args ) > i + 1 ):
                    j = i + 1
                    while ( j < len( command_args ) and not errors ):
                        arg = command_args[j]
                        if arg in arg_options:
                            break;
                        if ( not path.exists( arg ) or not path.isdir( arg ) ):
                            errors = "ERROR: The additional include directory %s does not exist."%( arg )
                        else:
                            if ( arg[len(arg)-1] != '/' ):
                                arg += '/'
                        self.command_data['additional_includes'].append( command_args[j] )
                        j+=1
                    i=j
                else:
                    errors = "ERROR: found -i option with no additional includes specified\nTry -h for usage"
            else:
                errors = "ERROR: Unknown arg %s\nTry -h for usage"%(arg)
        if not errors and not self.command_data['headers_to_mock']:
            errors = "ERROR: No header(s) to be mocked were specified\nTry -h for usage"
        return errors

<|endoftext|>