Initial Configuration Push

This commit is contained in:
CCOSTAN
2016-10-11 16:42:06 +00:00
parent b83eeadfcb
commit 5127bc2109
2145 changed files with 298464 additions and 0 deletions

1
.HA_VERSION Executable file
View File

@@ -0,0 +1 @@
0.30.1

12
.gitignore vendored Executable file
View File

@@ -0,0 +1,12 @@
*.pid
*.xml
OZW_Log.txt
home-assistant.log
home-assistant_v2.db
*.db-journal
lib
secrets.yaml
known_devices.yaml
phue.conf
pyozw.sqlite

19
README.md Normal file → Executable file
View File

@@ -1,2 +1,21 @@
# Home-AssistantConfig
Home Assistant configuration files (YAMLs)
This is my Home Assistant Configuration. I update it pretty regularly.
Home Assistantruns on my Raspberry Pi 3 with Z Wave Stick. I've also added a 433Mhz Transmitter and receiver.
Software on the Pi : Home Assistant, Dasher, HomeBridge
Devices I have :
* Lots of iOS Devices
* Nest Thermostat
* Amazon Echo
* Phillips Hue Hub
* Circle by Disney
* Rachio Sprinkler system
* SkyBell HD
* Rokus for all streaming
* ChromeCast Audios
* Etekcity Outlets
* Amazon Dash Buttons

528
configuration.yaml Executable file
View File

@@ -0,0 +1,528 @@
homeassistant:
name: Bear Stone Run
latitude: !secret homeassistant_latitude
longitude: !secret homeassistant_longitude
elevation: !secret homeassistant_elevation
unit_system: imperial
time_zone: America/New_York
customize:
climate.downstairs:
friendly_name: 'Nest Downstairs'
icon: mdi:air-conditioner
climate.upstairs:
friendly_name: 'Nest Upstairs'
icon: mdi:air-conditioner
sensor.dark_sky_precip_intensity:
friendly_name: 'Rainfall'
sensor.dark_sky_humidity:
friendly_name: 'Outdoor Humidity'
sensor.dark_sky_temperature:
friendly_name: 'Outdoor Temp'
sensor.speedtest_download:
friendly_name: 'Download'
icon: mdi:speedometer
sensor.speedtest_upload:
friendly_name: 'Upload'
icon: mdi:speedometer
media_player.roku_1gj361038190:
friendly_name: 'Downstairs Main Roku'
media_player.roku_1gs3ac111661:
friendly_name: 'Upstairs Roku'
media_player.roku_2N006T621680:
friendly_name: 'Bedroom Roku'
sensor.wii:
icon: mdi:gamepad-variant
sensor.tablotv:
icon: mdi:television-guide
sensor.hue_hub:
icon: mdi:router-wireless
sensor.rachio:
icon: mdi:spray
sensor.circle:
icon: mdi:google-circles-group
sensor.alexa_echo:
icon: mdi:amazon
sensor.skybell:
icon: mdi:camera-front
sensor.samsungtv:
icon: mdi:television
sensor.since_last_boot_templated:
friendly_name: 'HomeAssistant Uptime'
icon: mdi:clock-start
sensor.since_last_boot:
hidden: true
sensor.badlogin:
hidden: true
device_tracker.tablotv:
hidden: true
device_tracker.hue_hub:
hidden: true
device_tracker.wii:
hidden: true
device_tracker.rachio:
hidden: true
device_tracker.circle:
hidden: true
device_tracker.alexa_echo:
hidden: true
device_tracker.skybell:
hidden: true
device_tracker.samsungtv:
hidden: true
http:
api_password: !secret http_api_password
discovery:
light:
platform: hue
host: 192.168.10.101
allow_unreachable: true
ifttt:
key: !secret ifttt_key
device_tracker:
platform: nmap_tracker
hosts: 192.168.10.100-254
track_new_devices: no
# consider_home: 1800 - Added to known_devices.yaml instead on a device by device basis.
frontend:
# history:
logbook:
exclude:
entities:
- sensor.since_last_boot
- sensor.since_last_boot_templated
#logger:
# default: info
sun:
mqtt:
broker: 127.0.0.1
port: 1883
client_id: home-assistant-1
username: pi
password: raspberry
zwave:
usb_path: /dev/ttyACM0
config_path: /srv/hass/hass_venv/lib/python3.4/site-packages/libopenzwave-0.3.1-py3.4-linux-armv7l.egg/config
sensor forecast:
platform: darksky
api_key: !secret forecast_key
monitored_conditions:
- summary
# - precip_type
- precip_intensity
- temperature
# - dew_point
# - wind_speed
# - wind_bearing
# - cloud_cover
- humidity
# - pressure
# - visibility
# - ozone
nest:
username: !secret nest_username
password: !secret nest_password
climate:
platform: nest
media_player:
platform: cast
recorder:
purge_days: 14
sensor Speedtest:
platform: speedtest
minute: 30
hour:
- 0
- 6
- 12
- 18
monitored_conditions:
- download
- upload
# emulated_hue:
# host_ip: 192.168.10.10
# listen_port: 8300
# off_maps_to_on_domains:
# - script
# - scene
# A comma separated list of states that have to be tracked as a single group
# Grouped states should share the same type of states (ON/OFF or HOME/NOT_HOME)
group:
Family:
- device_tracker.carlo
- device_tracker.stacey
- device_tracker.franco
- device_tracker.yolanda
- device_tracker.joyce_ipad
Devices:
- sensor.since_last_boot_templated
- sensor.wii
- sensor.hue_hub
- sensor.tablotv
- sensor.alexa_echo
- sensor.circle
- sensor.rachio
- sensor.skybell
- sensor.samsungtv
Internet:
- sensor.speedtest_download
- sensor.speedtest_upload
Sensors:
- binary_sensor._sensor_2
- binary_sensor._sensor_3
- binary_sensor._sensor_5
- binary_sensor.aeotec_dsb04100_doorwindow_sensor_sensor_4
- binary_sensor.aeotec_dsb04100_doorwindow_sensor_sensor_6
Nest:
- climate.downstairs
- climate.upstairs
switch:
platform: rpi_rf
gpio: 17
switches:
Outlet_Living_Room:
protocol: 1
pulselength: 186
code_on: 5265155
code_off: 5265164
Outlet_2:
protocol: 1
pulselength: 186
code_on: 5264835
code_off: 5264844
Outlet_Garage:
protocol: 1
pulselength: 186
code_on: 5264691
code_off: 5264700
sensor Devices:
platform: template
sensors:
wii:
friendly_name: 'Wii'
value_template: >-
{%- if is_state("device_tracker.wii", "home") %}
Online
{% else %}
Offline
{%- endif %}
tablotv:
friendly_name: 'Tablo TV'
value_template: >-
{%- if is_state("device_tracker.tablotv", "home") %}
Online
{% else %}
Offline
{%- endif %}
hue_hub:
friendly_name: 'Hue Hub'
value_template: >-
{%- if is_state("device_tracker.hue_hub", "home") %}
Online
{% else %}
Offline
{%- endif %}
alexa_echo:
friendly_name: 'Alexa Echo'
value_template: >-
{%- if is_state("device_tracker.alexa_echo", "home") %}
Online
{% else %}
Offline
{%- endif %}
circle:
friendly_name: 'Disney Circle'
value_template: >-
{%- if is_state("device_tracker.circle", "home") %}
Online
{% else %}
Offline
{%- endif %}
rachio:
friendly_name: 'Rachio Sprinklers'
value_template: >-
{%- if is_state("device_tracker.rachio", "home") %}
Online
{% else %}
Offline
{%- endif %}
skybell:
friendly_name: 'Skybell Doorbell'
value_template: >-
{%- if is_state("device_tracker.skybell", "home") %}
Online
{% else %}
Offline
{%- endif %}
samsungtv:
friendly_name: 'Samsung TV'
value_template: >-
{%- if is_state("device_tracker.samsungtv", "home") %}
Online
{% else %}
Offline
{%- endif %}
sensor Login_Failures:
platform: command_line
command: "grep -c invalidpassword /home/hass/.homeassistant/home-assistant.log"
name: badlogin
sensor:
- platform: systemmonitor
resources:
# - type: last_boot
- type: since_last_boot
- platform: template
sensors:
since_last_boot_templated:
value_template: >-
{%- set slb = states.sensor.since_last_boot.state.split(' ') -%}
{%- set count = slb | length -%}
{%- set hms = slb[count - 1] -%}
{%- set hms_trimmed = hms.split('.')[0] -%}
{%- set hms_split = hms_trimmed.split(':') -%}
{%- set hours = hms_split[0] | int -%}
{%- set minutes = hms_split[1] | int -%}
{%- set seconds = hms_split[2] | int -%}
{%- if count == 3 -%}
{{ slb[0] ~ ' ' ~ slb[1] ~ ' ' }}
{%- endif -%}
{%- if hours > 0 -%}
{%- if hours == 1 -%}
1 hour
{%- else -%}
{{ hours }} hours
{%- endif -%}
{%- endif -%}
{%- if minutes > 0 -%}
{%- if hours > 0 -%}
{{ ', ' }}
{%- endif -%}
{%- if minutes == 1 -%}
1 minute
{%- else -%}
{{ minutes }} minutes
{%- endif -%}
{%- endif -%}
{%- if seconds > 0 -%}
{%- if hours > 0 or minutes > 0 -%}
{{ ', ' }}
{%- endif -%}
{%- if seconds == 1 -%}
1 second
{%- else -%}
{{ seconds }} seconds
{%- endif -%}
{%- endif -%}
automation:
- alias: Heal ZWave Nightly
trigger:
platform: time
after: '2:31:00'
action:
service: zwave.heal_network
- alias: "Update Available Notification"
trigger:
platform: state
entity_id: updater.updater
action:
service: ifttt.trigger
data: {"event":"device_status", "value1":"Home Assistant Update: ", "value2":"Available"}
- alias: Login Failure
trigger:
platform: numeric_state
entity_id: sensor.badlogin
above: 1
action:
service: ifttt.trigger
data: {"event":"device_status", "value1":"Home Assistant Error: ", "value2":"Login Failure Detected"}
- alias: 'Device Status'
#This recipe sends a POST to IFTTT Maker channel. IFTTT then sends me a SMS Text with "device_status : Wii is Offline."
trigger:
- platform: state
entity_id:
- sensor.wii
- sensor.tablotv
- sensor.hue_hub
- sensor.alexa_echo
- sensor.rachio
- sensor.circle
- sensor.skybell
action:
service: ifttt.trigger
data_template: {"event":"device_status", "value1":"{{ trigger.entity_id.split('.')[1] }}", "value2":"{{ trigger.to_state.state }}"}
- alias: Startup Notification
trigger:
platform: event
event_type: homeassistant_start
action:
service: ifttt.trigger
data: {"event":"device_status", "value1":"Home Assistant", "value2":"Up and Running"}
- alias: 'GoodNight - Away Mode'
# There is also an IFTTT recipe that shuts down all lights when Nest goes into Away mode. - event_type= Good_Night
trigger:
- platform: state
entity_id: group.family
state: 'not_home'
- platform: event
event_type: good_night
condition:
condition: state
entity_id: group.family
state: not_home
action:
service: light.turn_off
entity_id: group.all_lights
- alias: ZWave Enerwave Door Sensors Open
trigger:
platform: event
event_type: zwave.node_event
event_data:
object_id: enerwave_unknown_type0601_id0903_2
basic_level: 255
action:
service: light.turn_off
entity_id: light.office_lamp
- alias: ZWave Enerwave Door Sensors Closed
trigger:
platform: event
event_type: zwave.node_event
event_data:
object_id: enerwave_unknown_type0601_id0903_2
basic_level: 0
action:
service: light.turn_on
entity_id: light.office_lamp
- alias: TV Time
trigger:
- platform: sun
event: sunset
offset: '+00:30:00'
- platform: event
event_type: tv_time
condition:
condition: state
entity_id: group.family
state: home
action:
service: scene.turn_on
entity_id: scene.living_room_tv_time
scene:
- name: Living Room TV Time
entities:
light.dinette_light_1:
state: off
transition: 10
light.dinette_light_2:
state: off
transition: 10
light.living_room_front_left:
state: off
transition: 10
light.living_room_front_right:
state: off
transition: 10
light.living_room_slider:
state: off
transition: 10
light.living_room_back_right:
state: on
transition: 10
brightness: 1
light.living_room_back_left:
state: on
transition: 40
brightness: 1
light.living_room_couch_1:
state: on
transition: 400
xy_color: [0.6621,0.3023]
brightness: 255
light.living_room_couch_2:
state: on
transition: 400
xy_color: [0.6621,0.3023]
brightness: 255
light.couch_tv_light:
state: on
transition: 400
xy_color: [0.6621,0.3023]
brightness: 100
# Restart Homebridge on HASS start
# shell_command:
# restart_homebridge: 'sudo su pi -c "pm2 restart homebridge"'
# start_homebridge: 'sudo su pi -c "pm2 start homebridge"'
# stop_homebridge: 'sudo su pi -c "pm2 stop homebridge"'
### Future Ideas
# - alias: 'Get Random Time'
# trigger:
# platform: time
# after: '21:00:00'
# action:
# - service: input_slider.select_value
# data_template:
# entity_id: input_slider.hour
# value: '{{ (range(22, 23) | random) }}'
# - service: input_slider.select_value
# data_template:
# entity_id: input_slider.random_minute
# value: '{{ (range(30, 45) | random) }}'
# Then simply use that in your light turn off automation:
# - alias: 'Turn lights off'
# trigger:
# platform: template
# value_template: '{{ now.hour == (states.input_slider.random_hour.state | int) and now.minute == (states.input_slider.random_minute.state | int) }}'
# action:
# - service: light.turn_off
# data:
# entity_id: light.hue_color_lamp_1
#Todo List
# AUTOMATE LAMP UPSTAIRS USING ifttt AND Nest thermostat.
# Put Dash Buttons out there.
# Put door sensor on garage door

BIN
deps/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,3 @@
CherryPy is a pythonic, object-oriented HTTP framework

View File

@@ -0,0 +1 @@
pip

44
deps/CherryPy-8.1.0.dist-info/METADATA vendored Normal file
View File

@@ -0,0 +1,44 @@
Metadata-Version: 2.0
Name: CherryPy
Version: 8.1.0
Summary: Object-Oriented HTTP framework
Home-page: http://www.cherrypy.org
Author: CherryPy Team
Author-email: team@cherrypy.org
License: BSD
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: Freely Distributable
Classifier: Operating System :: OS Independent
Classifier: Framework :: CherryPy
Classifier: License :: OSI Approved :: BSD License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.1
Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: Implementation
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: Jython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Server
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Requires-Dist: six
Provides-Extra: This section defines feature flags end-users can use in dependenciesmemcached-session
Requires-Dist: python-memcached (>=1.58); extra == 'This section defines feature flags end-users can use in dependenciesmemcached-session'
CherryPy is a pythonic, object-oriented HTTP framework

242
deps/CherryPy-8.1.0.dist-info/RECORD vendored Normal file
View File

@@ -0,0 +1,242 @@
../../cherrypy/LICENSE.txt,sha256=IauyrgxBFcHp6BdR1ETnWowNhkQ8WGiakDvwZn4uMaw,1582
../../cherrypy/cherryd,sha256=NHP2aVxBaufEZ-xOtagQyikGYospC2E53zelFcQG4l8,101
../../cherrypy/favicon.ico,sha256=jrNK5SnKfbnSFgX_xQyLX3khmeImw8IbbHgJVIsGci0,1406
../../cherrypy/scaffold/example.conf,sha256=UXZHQHu71WKWzCei-Olo1gUreX8AcoW5exkUSi3bVaI,61
../../cherrypy/scaffold/site.conf,sha256=pjUhF-ir1xzSsV7LqXGfyR6Ns_r_n3ATWw8OlfbgT3w,426
../../cherrypy/scaffold/static/made_with_cherrypy_small.png,sha256=-A_xqPsK-9k4-36Uy1Uw4s7ufbs9cm30gQ8fb90JscA,7455
../../cherrypy/test/style.css,sha256=2Ypw_ziOWlY4dTZJlwsrorDLqLA1z485lgagaGemtKQ,17
../../cherrypy/test/test.pem,sha256=x6LrLPw2dBRyZwHXk6FhdSDNM3-Cv7DBXc8o4A19RhI,2254
../../cherrypy/test/static/dirback.jpg,sha256=naVXoM4eMycBnz1jxqlMLFVWkM9-DgFQRIYiCvjrCRo,18238
../../cherrypy/test/static/index.html,sha256=cB6ALrLhcxEGyMNgOHzmnBvmRnPCW_u3ebZUqdCiHkQ,14
../../cherrypy/tutorial/README.txt,sha256=6S48iS53EwxP16p7ttB0oDE0H6T_31WVhl5TQOnupuM,703
../../cherrypy/tutorial/custom_error.html,sha256=9cMEb83zwct9i-fJlyl7yvBSNexF7yEIWOoxH8lpllQ,404
../../cherrypy/tutorial/pdf_file.pdf,sha256=_4ED6K9omlqDodRtP1G3oagWwF4vqQbT1yjeA2UASgw,85698
../../cherrypy/tutorial/tutorial.conf,sha256=9ENgfRDyopHuignr_aHeMaWoC562xThbmlgF6zg4oEE,96
../../bin/cherryd,sha256=yoXgNxNxnyqQb35YPan1JBakxaktUYlclUaIsHsjOWA,87
CherryPy-8.1.0.dist-info/DESCRIPTION.rst,sha256=-2ZR2c39k2rBpsSnTMQh6aAYchh9RnvriVjF0BmLbOQ,57
CherryPy-8.1.0.dist-info/METADATA,sha256=yDpbX_a4VGrpOQM5-xdeQ8nVwx0shZbUCYXMoOAmKaE,2028
CherryPy-8.1.0.dist-info/RECORD,,
CherryPy-8.1.0.dist-info/WHEEL,sha256=rNo05PbNqwnXiIHFsYm0m22u4Zm6YJtugFG2THx4w3g,92
CherryPy-8.1.0.dist-info/metadata.json,sha256=pFeuWbZb0Q-cH1Q1w9Z9guuvR6ssDsrUWrQgHRgDxSc,1989
CherryPy-8.1.0.dist-info/top_level.txt,sha256=mOBE-r7Ej1kFrXNKlOj3yY9QfGA6Xkz6vZK7VNJF3YE,9
cherrypy/__init__.py,sha256=gFcXKfeonIhWgYdgTn2pzYkzYCy2EHuRjqIrKiAkAjw,11891
cherrypy/__main__.py,sha256=RC6ZmBSxqBfas6Y_BBYduvoTM933wRjqW5g24awvUcg,77
cherrypy/_cpchecker.py,sha256=EEVtXoJLtCgzh6N1xjX28MpTlvHwWlfK8hKscEPrp2Y,15034
cherrypy/_cpcompat.py,sha256=as8wX_SQ8y95PNellWx2EuUTXhNb3ddM_qG5CYxfe0o,9716
cherrypy/_cpconfig.py,sha256=77jqoLwfGIUwitNvCwrMb4ory0TbicTN7PwMbDEwYdE,10048
cherrypy/_cpdispatch.py,sha256=4X7qD6ujNWZFJQp9fRnur12CZUt0-TjHtpF_9JRiyF4,25303
cherrypy/_cperror.py,sha256=7eqrhVsyurxGPLh-PcKj187zkccJ6d-7qLYYo8SYCZk,22963
cherrypy/_cplogging.py,sha256=DJRDGJt1XZH9eSwPud2b30qdLyPwD5-VYMmbcFR47jg,17211
cherrypy/_cpmodpy.py,sha256=M2RpPP4MUSVT1n5GXsRi4yjsDB4AR-IOPJfp1W8x3fE,11158
cherrypy/_cpnative_server.py,sha256=919Jn2tGn2kpZhE0zU_S00oqcAnUeu1dey1UJJ9FOZA,5974
cherrypy/_cpreqbody.py,sha256=LbcCYkIp3VZeuzywySGtTLtlxv9ktI2BEN9VTb06xAE,37427
cherrypy/_cprequest.py,sha256=BXWubSLvtvRyTTLpj-IhoYl5-d_7fMMfmBnooGxHHvw,37198
cherrypy/_cpserver.py,sha256=TJ4KXCBeJBfXc5_kte1JkEutHKCIZnP707dtdu3FgGY,8277
cherrypy/_cpthreadinglocal.py,sha256=kZGe956xtkv9Gp6bkZxYuqUy_MY4WCGSuLHFFWkXWGE,6619
cherrypy/_cptools.py,sha256=RbMUwU13wdZoQB-R4KqsqaHfoo3e3YmE0nT3fO1Q17s,19426
cherrypy/_cptree.py,sha256=sbkOdp4uLIjEQrZeCC0wpHV3F-LV2uu7uRMRfDBlZzo,10394
cherrypy/_cpwsgi.py,sha256=bMHcT8USUtY8L5ReaNhvk2tqqS1o3h8nBngfcDNOZVE,16864
cherrypy/_cpwsgi_server.py,sha256=qcsgvqm2FjHOzu1N7DSzMk16LlseAM2WFPFVunbW_nA,3023
cherrypy/_helper.py,sha256=N0lfUrPU1RdfxXIYDpHccZ2LQAM3rNiJnrdxWDM7MSU,10338
cherrypy/daemon.py,sha256=dqtbnWyMZUrbvLfZItQT5aPyAAWOqmfv6K0_Q_VzSco,3913
cherrypy/lib/__init__.py,sha256=WeVRLlnimPVlecYFaOawcnlpPYVrkucteVKeHUoBYvY,2393
cherrypy/lib/auth.py,sha256=WA8TpNvWSoPAL3din7TVceR41uHJj6ZuaryNUEl081c,3224
cherrypy/lib/auth_basic.py,sha256=vYLRPP97S9jkNVGDeKDyU2lKt8yh-WoizcZu_QlQ_BY,3401
cherrypy/lib/auth_digest.py,sha256=mNRIiN9-QIuGVWqIQC-cUpbVWmdXdeyMASLaYsCGrR0,14178
cherrypy/lib/caching.py,sha256=DWovFBAwBmPPAi9jIevTEhvdrAz43uKoW27m7mkdTOI,17147
cherrypy/lib/covercp.py,sha256=LST3y1SNuVT9v14WMSIAGYerwFxAkVy65SxW9FRm1vI,11598
cherrypy/lib/cpstats.py,sha256=8mM-x5n3l604kLOHsm7bd3RtTtJaoeuYQo5twQoKG9A,23042
cherrypy/lib/cptools.py,sha256=o_dyD3_RKqy_Gp1zRZktmPi_HiGYqsanwWmMybduxxk,23756
cherrypy/lib/encoding.py,sha256=Bydtgx6mAtQzWD_9zEP90-fWUznd6-NllxoWmntJcg0,16240
cherrypy/lib/gctools.py,sha256=Rxk7SHrHuE1TMoJh48Q5NOmSmc_p8oPeVbuWWfmhayo,7344
cherrypy/lib/httpauth.py,sha256=qcah2nyHpBshXgwrcpf3FErZbpBp1L3hziYKbV5VlKk,13050
cherrypy/lib/httputil.py,sha256=noiOj5nW17Ol0RNumkcNO4fIzBw7tDd2Is-fmvIKpsg,17603
cherrypy/lib/jsontools.py,sha256=Jk79XKP9HZavy7LiEEUXKyg8UYywTnE5c7pvxhb6MLA,3924
cherrypy/lib/lockfile.py,sha256=xEt_WOr1y7fc2eG2Fj_yAzyRYJi6gUXPNzZpHVfEsSI,3448
cherrypy/lib/locking.py,sha256=H80gHcrHTret5i_29c7ISzf3_GyaNw2QsicDQbwo2iE,1224
cherrypy/lib/profiler.py,sha256=GsRoaZW_syTg4xlHZjNd_Od5xppoZRvDAAlkenxSDB8,6489
cherrypy/lib/reprconf.py,sha256=x9iYaaEXqrj-fqNSCJ5HDyyiPZqEq3S6va2GFrSFeAM,16186
cherrypy/lib/sessions.py,sha256=QHpfijremktJYm7MIm5UmjXe6TUGFoS62RW7dU8v0hw,30438
cherrypy/lib/static.py,sha256=8dc32u1c5JJ82WLG1iinS3hpxfM2QMln6Grv7Oa88E8,14960
cherrypy/lib/xmlrpcutil.py,sha256=QyCVvWReaqs6ZcnqgPfuLIYQyMUfWI2k2w5PMnPxUCw,1608
cherrypy/process/__init__.py,sha256=_kdPC_Wa66SIY6S4L14mZXSUFHPJK2q79bp6vyS9udk,552
cherrypy/process/plugins.py,sha256=CXQE3fvNM1d5vehVOH1i6se6PblXqo48JSn-LdFHmmY,26654
cherrypy/process/servers.py,sha256=9UyrJ___5fJfyoxoqEpGf8p_vnGPtm0B_VWpr8xJbDA,15727
cherrypy/process/win32.py,sha256=VIilNKDJrGuF4v1u5Ki8soLarKUGiHUdhVI5f-70QBs,5772
cherrypy/process/wspbus.py,sha256=JgrVeCzKq0MNajC9xW3zoyCHbZsTJrWO6ncZ7VTviT0,17156
cherrypy/scaffold/__init__.py,sha256=XIb5M6FokgALqxAbrH2vxquWpKJs1xm4Q85ptJ6XK20,1762
cherrypy/test/__init__.py,sha256=7vswU9gMLQpcAP01bgFC4TCmgfyUe5Z6cGW2POImxeM,692
cherrypy/test/_test_decorators.py,sha256=HxU9-DiUdrhfzxa2XOZQV2b5suCI6e18hqs15QBXhh4,992
cherrypy/test/_test_states_demo.py,sha256=8GC746zLbqPQCj6AplYSCa_kCkDDPyh3Tvuwi-Tk3jM,1925
cherrypy/test/benchmark.py,sha256=S7khs-SMv3KrD6e3LtJplxU4ROp5E_Kf9kfHnputr6E,12536
cherrypy/test/checkerdemo.py,sha256=VpLy7yTyqt8n3NErv4vFfn7q5xrhSghq0x3AzYT0VIY,1860
cherrypy/test/helper.py,sha256=E4DilAaMRaVO8ghNBD_Jcm8rFtjPTiRFkVW1lhM8pww,17573
cherrypy/test/logtest.py,sha256=AnFPL11lFnSlx82xEzMVedalTVO_-wrmRrX06AX83XU,7097
cherrypy/test/modfastcgi.py,sha256=2T3R9FCcsHVcssHfl3zRon_sQjpEEv_aRR-ZUy8Q-O4,4651
cherrypy/test/modfcgid.py,sha256=rX57myEw4HmRnSmeiYWnn9M4Gwp1DUie8NGyRzFHoBc,4236
cherrypy/test/modpy.py,sha256=0UxLfvq81J-0awar-jTZNV1mUv0wFOlWK7A-MI4IkQo,5009
cherrypy/test/modwsgi.py,sha256=COV2DMPmlLtpQ4TGIXLqy4bAqtHbonhhNrffXBScybI,4832
cherrypy/test/sessiondemo.py,sha256=GJCcNPb_-a_1dush4vGOotP7WjfPwweA4kJ6LWzyqio,5447
cherrypy/test/test_auth_basic.py,sha256=SCznMKX1dZaz_az_vs2381WV9vsYzRGJ-JgEBL8vyqg,3039
cherrypy/test/test_auth_digest.py,sha256=xipRY65noqfIuSeayk33xOZHCjhkmbH_ajtzhXnHZSE,5066
cherrypy/test/test_bus.py,sha256=sZmIGf693GPTqQFzNp4R4qLBU5iJa9GrUnicR9zA2hQ,9007
cherrypy/test/test_caching.py,sha256=IqiwMX3vlP6GPxlBcAKGWTkiOP59KKgNuQzpz-LqXb0,12587
cherrypy/test/test_compat.py,sha256=CEAwfFJjK6bXDnRmGlj-vhWeHH4Nl63YRZ8yhRuIZ68,474
cherrypy/test/test_config.py,sha256=1lcGqzylyBma8Itt3yML2XcZWlpeI3sOAR2g9XAEBiU,9179
cherrypy/test/test_config_server.py,sha256=V26tL8mpRmzG2ZosatDGiRzo47vp4MbbW6toIQzaZLI,4036
cherrypy/test/test_conn.py,sha256=CJK0RXKG2dw4ai9i29jzY-J0Sc9eHVax6L3MTTE425Y,30953
cherrypy/test/test_core.py,sha256=aZeMYYyl3YYfQWqWBAwgMk1Hp2pkz_izHtwVE9BWL9M,28039
cherrypy/test/test_dynamicobjectmapping.py,sha256=yTqw_mrm8Jzzsa1UCEUfZ-V5_gI4Up9vwdhgcwhmld8,12393
cherrypy/test/test_encoding.py,sha256=3Np9WtdKUNfLl6kIePeHlJrRCAeWkRv0QwCBJAnTo_4,16647
cherrypy/test/test_etags.py,sha256=VEa5oA1zD3PqYKfLzFYXNHtnb0IzXOqOlY-AGXMp_Sg,3093
cherrypy/test/test_http.py,sha256=b3bdpK49xufkwCVQFtAvZ4-HATDAlRIDpM9sucx3Jr8,9933
cherrypy/test/test_httpauth.py,sha256=1fKirgAkRcrluHzmWCrsr70RoHpaa6bCIIkiVODGuZI,6313
cherrypy/test/test_httplib.py,sha256=LHTbWmiglY1qW_40I_pLVcrmqeOxiq2ww6nTb9anTKU,1287
cherrypy/test/test_iterator.py,sha256=oi3Ja8BCNMC6s7cIYK1bbQLXZvmriwG2yR-vjERIYk0,5709
cherrypy/test/test_json.py,sha256=8-r2dIvQ_6IHADT6bA4pGMw6-unRWg998AXIKrNmcQs,2864
cherrypy/test/test_logging.py,sha256=4leoBQTGz7_75oHzch5vBwLB4jDu_GcGDypIMlSIzXM,6053
cherrypy/test/test_mime.py,sha256=37gS3iX0PcHLdm-EuPO01ZkyeFwlwiRsnbin9JpVnbw,4602
cherrypy/test/test_misc_tools.py,sha256=AbeWO7rznVs839Jhp6VWBDuzFkvgbZ7xAP8CZQcD_W4,7453
cherrypy/test/test_objectmapping.py,sha256=qiP1zq2BmG00HEAYpJzEsP9aZ-7KO0TnIa4sP-Tw20k,14521
cherrypy/test/test_params.py,sha256=M17ptv_JX0yQAwgE98cCwhXBZhllh1zCPgPQo_x_Cu8,1819
cherrypy/test/test_proxy.py,sha256=qb1Tp5cQfidZXgoDJ5DJEQGktvjJdFdwOmxhIwYsf2g,5062
cherrypy/test/test_refleaks.py,sha256=1qsVAJ4AOB8ma7BWpY9siGwpTUXG_b-CO-8K_udNpmg,1535
cherrypy/test/test_request_obj.py,sha256=gX2TwPC1pCfFZfb8Tv49g9dXWtK1XOuuQcv_UBK53Bk,32339
cherrypy/test/test_routes.py,sha256=63-JJS5PSwamgXHat1QKkuNKLINaLAvZc3hM8F4hn4M,2366
cherrypy/test/test_session.py,sha256=jWUj6Xm_UxRUNKIZe3afqCLzc3AofJzKpsfZjrevysU,17150
cherrypy/test/test_sessionauthenticate.py,sha256=Jcp9mjXMiAhMjS8NKEKtkkH0bFm6LTS6zUGHc31-hnM,2013
cherrypy/test/test_states.py,sha256=9Iz-lVx27Sj0oXo3LlmvLXz5EwoLhfMZvhJybqRSWTY,19091
cherrypy/test/test_static.py,sha256=rBbKTUrKIB7VBoLeTCXzfeGEaDb-85ky9h8G0kJT-zM,14051
cherrypy/test/test_tools.py,sha256=gi-N2WYNkXvv57VaYH8XVo0IyRbQFHCcO-4Fq6h0GZU,16899
cherrypy/test/test_tutorials.py,sha256=jq9-AMO0UicQ35IMERyrg22uYCR-oQhXPq8BUlpRzK4,7239
cherrypy/test/test_virtualhost.py,sha256=WDz1rD69PwRiGh6xNResy36o_ILDgBFlNZAcVyR5l_c,4020
cherrypy/test/test_wsgi_ns.py,sha256=2SLVbU4Fe1Znt1AC561fh06BM-FGpZGRcEPa3v4eARU,2868
cherrypy/test/test_wsgi_unix_socket.py,sha256=nkCl6LEohAI186awz65weREZ0o9BgKRdpDMaTVsMv6A,2635
cherrypy/test/test_wsgi_vhost.py,sha256=Wsi0bibrBUhJBOkPzuxy4-SELbMLpk7Hl6P_oND8Xfo,1034
cherrypy/test/test_wsgiapps.py,sha256=IatsMtVjrsHPBuM4e5nAGMZcdcs2sxJSxyDN_xJ6Usg,4190
cherrypy/test/test_xmlrpc.py,sha256=2fz5sFBwkW2HRPsrcxJ7gkTRrMJzQtG71ZhR-0ukPrk,5830
cherrypy/test/webtest.py,sha256=_JjzK2J8dLdIlvQwdh1rBMCIkUJpDgfMpiHTMKRGqgo,20399
cherrypy/tutorial/__init__.py,sha256=cmLXfvQI0L6trCXwDzR0WE1bu4JZYt301HJRNhjZOBc,85
cherrypy/tutorial/tut01_helloworld.py,sha256=MRt9GwP9IoUcXOG4Cq1-Asge1ToX1kvTjpLY5U-5VyI,1014
cherrypy/tutorial/tut02_expose_methods.py,sha256=kYSTvBRXBDE-uQekMW66rVTRaVC2rS3UySyEi_KSNS8,799
cherrypy/tutorial/tut03_get_and_post.py,sha256=VTXZfGr7KRzo-NXG713RsHh4FEqxjoS3agzoy15Ksuo,1586
cherrypy/tutorial/tut04_complex_site.py,sha256=TZSRXINZVJIgTPnWtH4uunwjROZIid0QWLxNcpoySM4,2947
cherrypy/tutorial/tut05_derived_objects.py,sha256=N9w8D2ohznvBbPdWb5OhkQLcidDCiLuMBMtJIX8ofp0,2140
cherrypy/tutorial/tut06_default_method.py,sha256=AcIdYCpd08czHL7VdeCI_k4t7dALobxledk0ss8RHPk,2263
cherrypy/tutorial/tut07_sessions.py,sha256=jSoXprvwLUxvR7QiuyCD0bHLDVYRlJk4mpNWTYnqrWU,1227
cherrypy/tutorial/tut08_generators_and_yield.py,sha256=GWIWPhkQ9v5v6ReNjoZhPvnoK6H0_mUw1vut87WWoHE,1287
cherrypy/tutorial/tut09_files.py,sha256=tTk_CCgQrv8xWr-L-YgUx5mQD6HELYvfJ3uBhtY_wIQ,3462
cherrypy/tutorial/tut10_http_errors.py,sha256=NWCe8DJrNLRghNUdTRB1Ge_2dBvgYBCyv8QGNzpfHw4,2705
cherrypy/wsgiserver/__init__.py,sha256=Hf2IefJVSLv45C8j7-JwrQS_CGP0_NTAZQ3WdXMQTmI,92609
cherrypy/wsgiserver/ssl_builtin.py,sha256=MCCgEbSg1SVmi4AaYyghoSBqT-q99YxZcK9M5_D6D8A,3833
CherryPy-8.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
cherrypy/test/__pycache__/benchmark.cpython-34.pyc,,
cherrypy/test/__pycache__/test_json.cpython-34.pyc,,
cherrypy/test/__pycache__/test_config_server.cpython-34.pyc,,
cherrypy/test/__pycache__/test_httpauth.cpython-34.pyc,,
cherrypy/test/__pycache__/test_misc_tools.cpython-34.pyc,,
cherrypy/test/__pycache__/helper.cpython-34.pyc,,
cherrypy/lib/__pycache__/reprconf.cpython-34.pyc,,
cherrypy/test/__pycache__/test_auth_basic.cpython-34.pyc,,
cherrypy/lib/__pycache__/auth_digest.cpython-34.pyc,,
cherrypy/__pycache__/__main__.cpython-34.pyc,,
cherrypy/process/__pycache__/plugins.cpython-34.pyc,,
cherrypy/__pycache__/_helper.cpython-34.pyc,,
cherrypy/tutorial/__pycache__/tut03_get_and_post.cpython-34.pyc,,
cherrypy/lib/__pycache__/jsontools.cpython-34.pyc,,
cherrypy/test/__pycache__/test_conn.cpython-34.pyc,,
cherrypy/__pycache__/_cptools.cpython-34.pyc,,
cherrypy/__pycache__/daemon.cpython-34.pyc,,
cherrypy/wsgiserver/__pycache__/ssl_builtin.cpython-34.pyc,,
cherrypy/test/__pycache__/test_xmlrpc.cpython-34.pyc,,
cherrypy/test/__pycache__/sessiondemo.cpython-34.pyc,,
cherrypy/test/__pycache__/test_caching.cpython-34.pyc,,
cherrypy/__pycache__/_cptree.cpython-34.pyc,,
cherrypy/__pycache__/_cpreqbody.cpython-34.pyc,,
cherrypy/lib/__pycache__/static.cpython-34.pyc,,
cherrypy/lib/__pycache__/lockfile.cpython-34.pyc,,
cherrypy/__pycache__/_cplogging.cpython-34.pyc,,
cherrypy/__pycache__/_cprequest.cpython-34.pyc,,
cherrypy/test/__pycache__/test_proxy.cpython-34.pyc,,
cherrypy/tutorial/__pycache__/tut02_expose_methods.cpython-34.pyc,,
cherrypy/process/__pycache__/servers.cpython-34.pyc,,
cherrypy/test/__pycache__/test_auth_digest.cpython-34.pyc,,
cherrypy/tutorial/__pycache__/tut08_generators_and_yield.cpython-34.pyc,,
cherrypy/test/__pycache__/modpy.cpython-34.pyc,,
cherrypy/lib/__pycache__/locking.cpython-34.pyc,,
cherrypy/test/__pycache__/logtest.cpython-34.pyc,,
cherrypy/__pycache__/_cperror.cpython-34.pyc,,
cherrypy/test/__pycache__/_test_states_demo.cpython-34.pyc,,
cherrypy/test/__pycache__/test_mime.cpython-34.pyc,,
cherrypy/test/__pycache__/test_core.cpython-34.pyc,,
cherrypy/test/__pycache__/test_objectmapping.cpython-34.pyc,,
cherrypy/scaffold/__pycache__/__init__.cpython-34.pyc,,
cherrypy/tutorial/__pycache__/tut10_http_errors.cpython-34.pyc,,
cherrypy/test/__pycache__/modfastcgi.cpython-34.pyc,,
cherrypy/test/__pycache__/test_dynamicobjectmapping.cpython-34.pyc,,
cherrypy/test/__pycache__/test_states.cpython-34.pyc,,
cherrypy/__pycache__/_cpnative_server.cpython-34.pyc,,
cherrypy/tutorial/__pycache__/tut07_sessions.cpython-34.pyc,,
cherrypy/test/__pycache__/test_params.cpython-34.pyc,,
cherrypy/test/__pycache__/test_tutorials.cpython-34.pyc,,
cherrypy/test/__pycache__/test_session.cpython-34.pyc,,
cherrypy/test/__pycache__/test_tools.cpython-34.pyc,,
cherrypy/lib/__pycache__/auth.cpython-34.pyc,,
cherrypy/lib/__pycache__/sessions.cpython-34.pyc,,
cherrypy/test/__pycache__/test_wsgi_ns.cpython-34.pyc,,
cherrypy/__pycache__/_cpmodpy.cpython-34.pyc,,
cherrypy/process/__pycache__/wspbus.cpython-34.pyc,,
cherrypy/lib/__pycache__/httputil.cpython-34.pyc,,
cherrypy/test/__pycache__/test_httplib.cpython-34.pyc,,
cherrypy/test/__pycache__/test_static.cpython-34.pyc,,
cherrypy/lib/__pycache__/xmlrpcutil.cpython-34.pyc,,
cherrypy/test/__pycache__/test_config.cpython-34.pyc,,
cherrypy/test/__pycache__/test_routes.cpython-34.pyc,,
cherrypy/__pycache__/_cpwsgi_server.cpython-34.pyc,,
cherrypy/__pycache__/_cpconfig.cpython-34.pyc,,
cherrypy/test/__pycache__/test_logging.cpython-34.pyc,,
cherrypy/tutorial/__pycache__/tut05_derived_objects.cpython-34.pyc,,
cherrypy/test/__pycache__/checkerdemo.cpython-34.pyc,,
cherrypy/__pycache__/_cpcompat.cpython-34.pyc,,
cherrypy/tutorial/__pycache__/tut06_default_method.cpython-34.pyc,,
cherrypy/test/__pycache__/test_encoding.cpython-34.pyc,,
cherrypy/test/__pycache__/test_wsgi_unix_socket.cpython-34.pyc,,
cherrypy/test/__pycache__/modfcgid.cpython-34.pyc,,
cherrypy/test/__pycache__/test_request_obj.cpython-34.pyc,,
cherrypy/test/__pycache__/test_bus.cpython-34.pyc,,
cherrypy/lib/__pycache__/auth_basic.cpython-34.pyc,,
cherrypy/lib/__pycache__/covercp.cpython-34.pyc,,
cherrypy/test/__pycache__/test_refleaks.cpython-34.pyc,,
cherrypy/wsgiserver/__pycache__/__init__.cpython-34.pyc,,
cherrypy/test/__pycache__/test_wsgi_vhost.cpython-34.pyc,,
cherrypy/test/__pycache__/modwsgi.cpython-34.pyc,,
cherrypy/test/__pycache__/test_etags.cpython-34.pyc,,
cherrypy/test/__pycache__/test_compat.cpython-34.pyc,,
cherrypy/test/__pycache__/test_wsgiapps.cpython-34.pyc,,
cherrypy/test/__pycache__/test_http.cpython-34.pyc,,
cherrypy/lib/__pycache__/gctools.cpython-34.pyc,,
cherrypy/lib/__pycache__/encoding.cpython-34.pyc,,
cherrypy/__pycache__/_cpwsgi.cpython-34.pyc,,
cherrypy/test/__pycache__/test_iterator.cpython-34.pyc,,
cherrypy/test/__pycache__/__init__.cpython-34.pyc,,
cherrypy/lib/__pycache__/profiler.cpython-34.pyc,,
cherrypy/lib/__pycache__/cpstats.cpython-34.pyc,,
cherrypy/tutorial/__pycache__/tut09_files.cpython-34.pyc,,
cherrypy/tutorial/__pycache__/tut04_complex_site.cpython-34.pyc,,
cherrypy/tutorial/__pycache__/tut01_helloworld.cpython-34.pyc,,
cherrypy/__pycache__/__init__.cpython-34.pyc,,
cherrypy/process/__pycache__/win32.cpython-34.pyc,,
cherrypy/__pycache__/_cpthreadinglocal.cpython-34.pyc,,
cherrypy/tutorial/__pycache__/__init__.cpython-34.pyc,,
cherrypy/lib/__pycache__/cptools.cpython-34.pyc,,
cherrypy/test/__pycache__/test_sessionauthenticate.cpython-34.pyc,,
cherrypy/test/__pycache__/_test_decorators.cpython-34.pyc,,
cherrypy/__pycache__/_cpdispatch.cpython-34.pyc,,
cherrypy/__pycache__/_cpchecker.cpython-34.pyc,,
cherrypy/lib/__pycache__/httpauth.cpython-34.pyc,,
cherrypy/lib/__pycache__/__init__.cpython-34.pyc,,
cherrypy/test/__pycache__/webtest.cpython-34.pyc,,
cherrypy/lib/__pycache__/caching.cpython-34.pyc,,
cherrypy/process/__pycache__/__init__.cpython-34.pyc,,
cherrypy/__pycache__/_cpserver.cpython-34.pyc,,
cherrypy/test/__pycache__/test_virtualhost.cpython-34.pyc,,

5
deps/CherryPy-8.1.0.dist-info/WHEEL vendored Normal file
View File

@@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.29.0)
Root-Is-Purelib: true
Tag: py3-none-any

View File

@@ -0,0 +1 @@
{"classifiers": ["Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: Freely Distributable", "Operating System :: OS Independent", "Framework :: CherryPy", "License :: OSI Approved :: BSD License", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.1", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: Implementation", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: Jython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Internet :: WWW/HTTP :: HTTP Servers", "Topic :: Internet :: WWW/HTTP :: WSGI", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", "Topic :: Internet :: WWW/HTTP :: WSGI :: Server", "Topic :: Software Development :: Libraries :: Application Frameworks"], "extensions": {"python.details": {"contacts": [{"email": "team@cherrypy.org", "name": "CherryPy Team", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://www.cherrypy.org"}}}, "extras": ["This section defines feature flags end-users can use in dependenciesmemcached-session"], "generator": "bdist_wheel (0.29.0)", "license": "BSD", "metadata_version": "2.0", "name": "CherryPy", "run_requires": [{"extra": "This section defines feature flags end-users can use in dependenciesmemcached-session", "requires": ["python-memcached (>=1.58)"]}, {"requires": ["six"]}], "summary": "Object-Oriented HTTP framework", "test_requires": [{"requires": ["tox"]}], "version": "8.1.0"}

View File

@@ -0,0 +1 @@
cherrypy

View File

@@ -0,0 +1,160 @@
pychromecast |Build Status|
===========================
.. |Build Status| image:: https://travis-ci.org/balloob/pychromecast.svg?branch=master
:target: https://travis-ci.org/balloob/pychromecast
Library for Python 2 and 3 to communicate with the Google Chromecast. It
currently supports:
- Auto discovering connected Chromecasts on the network
- Start the default media receiver and play any online media
- Control playback of current playing media
- Implement Google Chromecast api v2
- Communicate with apps via channels
- Easily extendable to add support for unsupported namespaces
- Multi-room setups with Audio cast devices
*PyChromecast 0.6 introduces some backward incompatible changes due to
the migration from DIAL to socket for retrieving the app status.*
Dependencies
------------
PyChromecast depends on the Python packages requests, protobuf and
zeroconf. Make sure you have these dependencies installed using
``pip install -r requirements.txt``
Some users running Python 2.7 have `reported`_ that they had to upgrade
their version of pip using ``pip install --upgrade pip`` before they
were able to install the latest version of the dependencies.
.. _reported: https://github.com/balloob/pychromecast/issues/47#issuecomment-107822162
How to use
----------
.. code:: python
>> from __future__ import print_function
>> import time
>> import pychromecast
>> pychromecast.get_chromecasts_as_dict().keys()
['Dev', 'Living Room', 'Den', 'Bedroom']
>> cast = pychromecast.get_chromecast(friendly_name="Living Room")
>> # Wait for cast device to be ready
>> cast.wait()
>> print(cast.device)
DeviceStatus(friendly_name='Living Room', model_name='Chromecast', manufacturer='Google Inc.', api_version=(1, 0), uuid=UUID('df6944da-f016-4cb8-97d0-3da2ccaa380b'), cast_type='cast')
>> print(cast.status)
CastStatus(is_active_input=True, is_stand_by=False, volume_level=1.0, volume_muted=False, app_id=u'CC1AD845', display_name=u'Default Media Receiver', namespaces=[u'urn:x-cast:com.google.cast.player.message', u'urn:x-cast:com.google.cast.media'], session_id=u'CCA39713-9A4F-34A6-A8BF-5D97BE7ECA5C', transport_id=u'web-9', status_text='')
>> mc = cast.media_controller
>> mc.play_media('http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4', 'video/mp4')
>> print(mc.status)
MediaStatus(current_time=42.458322, content_id=u'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4', content_type=u'video/mp4', duration=596.474195, stream_type=u'BUFFERED', idle_reason=None, media_session_id=1, playback_rate=1, player_state=u'PLAYING', supported_media_commands=15, volume_level=1, volume_muted=False)
>> mc.pause()
>> time.sleep(5)
>> mc.play()
Adding support for extra namespaces
-----------------------------------
Each app that runs on the Chromecast supports namespaces. They specify a
JSON-based mini-protocol. This is used to communicate between the
Chromecast and your phone/browser and now Python.
Support for extra namespaces is added by using controllers. To add your own namespace to a current chromecast instance you will first have to define your controller. Example of a minimal controller:
.. code:: python
from pychromecast.controllers import BaseController
class MyController(BaseController):
def __init__(self):
super(MyController, self).__init__(
"urn:x-cast:my.super.awesome.namespace")
def receive_message(self, message, data):
print("Wow, I received this message: {}".format(data))
return True # indicate you handled this message
def request_beer(self):
self.send_message({'request': 'beer'})
After you have defined your controller you will have to add an instance to a Chromecast object: `cast.register_handler(MyController())`. When a message is received with your namespace it will be routed to your controller.
For more options see the `BaseController`_. For an example of a fully implemented controller see the `MediaController`_.
.. _BaseController: https://github.com/balloob/pychromecast/blob/master/pychromecast/controllers/__init__.py
.. _MediaController: https://github.com/balloob/pychromecast/blob/master/pychromecast/controllers/media.py
Exploring existing namespaces
-------------------------------
So you've got PyChromecast running and decided it is time to add support to your favorite app. No worries, the following instructions will have you covered in exploring the possibilities.
The following instructions require the use of the `Google Chrome browser`_ and the `Google Cast plugin`_.
* In Chrome, go to `chrome://net-internals/#capture`
* Enable the checkbox 'Include the actual bytes sent/received.'
* Open a new tab, browse to your favorite application on the web that has Chromecast support and start casting.
* Go back to the tab that is capturing events and click on stop.
* From the dropdown click on events. This will show you a table with events that happened while you were recording.
* In the filter box enter the text `Tr@n$p0rt`. This should give one SOCKET connection as result: the connection with your Chromecast.
* Go through the results and collect the JSON that is exchanged.
* Now write a controller that is able to mimic this behavior :-)
.. _Google Chrome Browser: https://www.google.com/chrome/
.. _Google Cast Plugin: https://chrome.google.com/webstore/detail/google-cast/boadgeojelhgndaghljhdicfkmllpafd
Ignoring CEC Data
-----------------
The Chromecast typically reports whether it is the active input on the device
to which it is connected. This value is stored inside a cast object in the
following property.
.. code:: python
cast.status.is_active_input
Some Chromecast users have reported CEC incompatibilities with their media
center devices. These incompatibilities may sometimes cause this active input
value to be reported improperly.
This active input value is typically used to determine if the Chromecast
is idle. PyChromecast is capable of ignoring the active input value when
determining if the Chromecast is idle in the instance that the
Chromecast is returning erroneous values. To ignore this CEC detection
data in PyChromecast, append a `Linux style wildcard`_ formatted string
to the IGNORE\_CEC list in PyChromecast like in the example below.
.. code:: python
pychromecast.IGNORE_CEC.append('*') # Ignore CEC on all devices
pychromecast.IGNORE_CEC.append('Living Room') # Ignore CEC on Chromecasts named Living Room
Maintainers
-----------
- Jan Borsodi (`@am0s`_)
- Ryan Kraus (`@rmkraus`_)
- Paulus Schoutsen (`@balloob`_, original author)
Thanks
------
I would like to thank `Fred Clift`_ for laying the socket client ground
work. Without him it would not have been possible!
.. _Linux style wildcard: http://tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm
.. _@am0s: https://github.com/am0s
.. _@rmkraus: https://github.com/rmkraus
.. _@balloob: https://github.com/balloob
.. _Fred Clift: https://github.com/minektur

View File

@@ -0,0 +1 @@
pip

View File

@@ -0,0 +1,181 @@
Metadata-Version: 2.0
Name: PyChromecast
Version: 0.7.4
Summary: Python module to talk to Google Chromecast.
Home-page: https://github.com/balloob/pychromecast
Author: Paulus Schoutsen
Author-email: paulus@paulusschoutsen.nl
License: MIT
Platform: any
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Dist: protobuf (==3.0.0b2)
Requires-Dist: requests (>=2.0)
Requires-Dist: six (>=1.10.0)
Requires-Dist: zeroconf (>=0.17.4)
pychromecast |Build Status|
===========================
.. |Build Status| image:: https://travis-ci.org/balloob/pychromecast.svg?branch=master
:target: https://travis-ci.org/balloob/pychromecast
Library for Python 2 and 3 to communicate with the Google Chromecast. It
currently supports:
- Auto discovering connected Chromecasts on the network
- Start the default media receiver and play any online media
- Control playback of current playing media
- Implement Google Chromecast api v2
- Communicate with apps via channels
- Easily extendable to add support for unsupported namespaces
- Multi-room setups with Audio cast devices
*PyChromecast 0.6 introduces some backward incompatible changes due to
the migration from DIAL to socket for retrieving the app status.*
Dependencies
------------
PyChromecast depends on the Python packages requests, protobuf and
zeroconf. Make sure you have these dependencies installed using
``pip install -r requirements.txt``
Some users running Python 2.7 have `reported`_ that they had to upgrade
their version of pip using ``pip install --upgrade pip`` before they
were able to install the latest version of the dependencies.
.. _reported: https://github.com/balloob/pychromecast/issues/47#issuecomment-107822162
How to use
----------
.. code:: python
>> from __future__ import print_function
>> import time
>> import pychromecast
>> pychromecast.get_chromecasts_as_dict().keys()
['Dev', 'Living Room', 'Den', 'Bedroom']
>> cast = pychromecast.get_chromecast(friendly_name="Living Room")
>> # Wait for cast device to be ready
>> cast.wait()
>> print(cast.device)
DeviceStatus(friendly_name='Living Room', model_name='Chromecast', manufacturer='Google Inc.', api_version=(1, 0), uuid=UUID('df6944da-f016-4cb8-97d0-3da2ccaa380b'), cast_type='cast')
>> print(cast.status)
CastStatus(is_active_input=True, is_stand_by=False, volume_level=1.0, volume_muted=False, app_id=u'CC1AD845', display_name=u'Default Media Receiver', namespaces=[u'urn:x-cast:com.google.cast.player.message', u'urn:x-cast:com.google.cast.media'], session_id=u'CCA39713-9A4F-34A6-A8BF-5D97BE7ECA5C', transport_id=u'web-9', status_text='')
>> mc = cast.media_controller
>> mc.play_media('http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4', 'video/mp4')
>> print(mc.status)
MediaStatus(current_time=42.458322, content_id=u'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4', content_type=u'video/mp4', duration=596.474195, stream_type=u'BUFFERED', idle_reason=None, media_session_id=1, playback_rate=1, player_state=u'PLAYING', supported_media_commands=15, volume_level=1, volume_muted=False)
>> mc.pause()
>> time.sleep(5)
>> mc.play()
Adding support for extra namespaces
-----------------------------------
Each app that runs on the Chromecast supports namespaces. They specify a
JSON-based mini-protocol. This is used to communicate between the
Chromecast and your phone/browser and now Python.
Support for extra namespaces is added by using controllers. To add your own namespace to a current chromecast instance you will first have to define your controller. Example of a minimal controller:
.. code:: python
from pychromecast.controllers import BaseController
class MyController(BaseController):
def __init__(self):
super(MyController, self).__init__(
"urn:x-cast:my.super.awesome.namespace")
def receive_message(self, message, data):
print("Wow, I received this message: {}".format(data))
return True # indicate you handled this message
def request_beer(self):
self.send_message({'request': 'beer'})
After you have defined your controller you will have to add an instance to a Chromecast object: `cast.register_handler(MyController())`. When a message is received with your namespace it will be routed to your controller.
For more options see the `BaseController`_. For an example of a fully implemented controller see the `MediaController`_.
.. _BaseController: https://github.com/balloob/pychromecast/blob/master/pychromecast/controllers/__init__.py
.. _MediaController: https://github.com/balloob/pychromecast/blob/master/pychromecast/controllers/media.py
Exploring existing namespaces
-------------------------------
So you've got PyChromecast running and decided it is time to add support to your favorite app. No worries, the following instructions will have you covered in exploring the possibilities.
The following instructions require the use of the `Google Chrome browser`_ and the `Google Cast plugin`_.
* In Chrome, go to `chrome://net-internals/#capture`
* Enable the checkbox 'Include the actual bytes sent/received.'
* Open a new tab, browse to your favorite application on the web that has Chromecast support and start casting.
* Go back to the tab that is capturing events and click on stop.
* From the dropdown click on events. This will show you a table with events that happened while you were recording.
* In the filter box enter the text `Tr@n$p0rt`. This should give one SOCKET connection as result: the connection with your Chromecast.
* Go through the results and collect the JSON that is exchanged.
* Now write a controller that is able to mimic this behavior :-)
.. _Google Chrome Browser: https://www.google.com/chrome/
.. _Google Cast Plugin: https://chrome.google.com/webstore/detail/google-cast/boadgeojelhgndaghljhdicfkmllpafd
Ignoring CEC Data
-----------------
The Chromecast typically reports whether it is the active input on the device
to which it is connected. This value is stored inside a cast object in the
following property.
.. code:: python
cast.status.is_active_input
Some Chromecast users have reported CEC incompatibilities with their media
center devices. These incompatibilities may sometimes cause this active input
value to be reported improperly.
This active input value is typically used to determine if the Chromecast
is idle. PyChromecast is capable of ignoring the active input value when
determining if the Chromecast is idle in the instance that the
Chromecast is returning erroneous values. To ignore this CEC detection
data in PyChromecast, append a `Linux style wildcard`_ formatted string
to the IGNORE\_CEC list in PyChromecast like in the example below.
.. code:: python
pychromecast.IGNORE_CEC.append('*') # Ignore CEC on all devices
pychromecast.IGNORE_CEC.append('Living Room') # Ignore CEC on Chromecasts named Living Room
Maintainers
-----------
- Jan Borsodi (`@am0s`_)
- Ryan Kraus (`@rmkraus`_)
- Paulus Schoutsen (`@balloob`_, original author)
Thanks
------
I would like to thank `Fred Clift`_ for laying the socket client ground
work. Without him it would not have been possible!
.. _Linux style wildcard: http://tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm
.. _@am0s: https://github.com/am0s
.. _@rmkraus: https://github.com/rmkraus
.. _@balloob: https://github.com/balloob
.. _Fred Clift: https://github.com/minektur

View File

@@ -0,0 +1,42 @@
.DS_Store,sha256=z2gRZJvEM_WGkHGXKJ65r7UPG7qICbiawVsPXEnp-8c,6148
PyChromecast-0.7.4.dist-info/DESCRIPTION.rst,sha256=SSmbtGe3rWS2588qTAdJ0VQvN9YzIT8RPIyhOgyxA6A,7126
PyChromecast-0.7.4.dist-info/METADATA,sha256=S_HTSuuPz09zMaXxGrFtgHo5C_-eWaS1GDEtJviJhPM,7866
PyChromecast-0.7.4.dist-info/RECORD,,
PyChromecast-0.7.4.dist-info/WHEEL,sha256=GrqQvamwgBV4nLoJe0vhYRSWzWsx7xjlt74FT0SWYfE,110
PyChromecast-0.7.4.dist-info/metadata.json,sha256=kJPpgubHP3GQOpcv9gIQcba_QDjnDxnjPh9tOJQHr0o,903
PyChromecast-0.7.4.dist-info/pbr.json,sha256=WvxHilx5XoxsLZlvp6nyc79cgMObi5TSKj6KUiSdTe4,47
PyChromecast-0.7.4.dist-info/top_level.txt,sha256=rO8Gp9im127G1t-Kzk0r2Lh2y19ZaIG91OBRZpw0gTA,13
pychromecast/.DS_Store,sha256=oDO_w-SWfCkAPjvaG6BL1qB2KvyzpPpFv8MbYH9fljg,6148
pychromecast/__init__.py,sha256=U3Y46fioyGyvkNNDaulVorIspSbFK6rpVPKeANWIPPY,15479
pychromecast/authority_keys_pb2.py,sha256=b4lL5q50P1OJNn-z2JvkUF3uoc347geHiFqeyV8kq-w,3985
pychromecast/cast_channel_pb2.py,sha256=JPXrpcOWoeNIOWf9-FZFooAfYLgrxSDKfiIuReCmCpk,16191
pychromecast/config.py,sha256=28hjJlQ2vzZbqS12VW6a42ry6NNo1khMJ9NDYyd2zm8,1012
pychromecast/dial.py,sha256=_-rSh3r8_MIWwfKY1Wqs7NEAKKcE0K0Vnhl1VVrO0KU,3877
pychromecast/discovery.py,sha256=1S5wvFaf-4WrRKfdfe7-stPEP8TLU2L6GeIAmnn36oU,2578
pychromecast/error.py,sha256=4tQQ-I4c2k0OgJWsn-kAcR1konUPq2DzfB6qtSJ2oUw,1334
pychromecast/logging_pb2.py,sha256=2vOWKAt7iBjp16sdOwpFvDSlHtzQqq13qoKR7bL6Dik,34008
pychromecast/socket_client.py,sha256=9uET2UNvjW4ra5RgdniWlJBhat54lqqh_wmpGhfWOHw,33787
pychromecast/upnp.py,sha256=A8WsgB5ay98e35T4b-UxxItcQVQOka4TICbi8JUv-mg,2732
pychromecast/websocket.py,sha256=9ydyIh7b7eoIA_yXvk-HRuHowhPd9bmQaoyLYgXn4y4,13417
pychromecast/youtube.py,sha256=2iHHo-VnvSjWY2xKjd8lGDqvg3sYpqaExWnx-Puccxc,1054
pychromecast/controllers/__init__.py,sha256=qUwhRxqUi8bRO-ejv2azlGbm776wh4iiWEIp4lJ-IkQ,3593
pychromecast/controllers/media.py,sha256=4HNKUTsejuRU2w8AtGLrifuN-mtYRW-HP-t5QIo28cU,16129
pychromecast/controllers/plex.py,sha256=u1C5WGkoqAlzGdvNF1nCo9riuDW9s_idxhfhY4ZupXQ,703
pychromecast/controllers/youtube.py,sha256=eTZb0qK4fua9ai3UQFDcJMcIMfLTAVPwWSlGIvUXI6Y,1293
PyChromecast-0.7.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
pychromecast/__pycache__/logging_pb2.cpython-34.pyc,,
pychromecast/__pycache__/authority_keys_pb2.cpython-34.pyc,,
pychromecast/__pycache__/dial.cpython-34.pyc,,
pychromecast/controllers/__pycache__/__init__.cpython-34.pyc,,
pychromecast/__pycache__/upnp.cpython-34.pyc,,
pychromecast/__pycache__/socket_client.cpython-34.pyc,,
pychromecast/__pycache__/error.cpython-34.pyc,,
pychromecast/controllers/__pycache__/youtube.cpython-34.pyc,,
pychromecast/__pycache__/config.cpython-34.pyc,,
pychromecast/__pycache__/cast_channel_pb2.cpython-34.pyc,,
pychromecast/controllers/__pycache__/plex.cpython-34.pyc,,
pychromecast/__pycache__/__init__.cpython-34.pyc,,
pychromecast/__pycache__/youtube.cpython-34.pyc,,
pychromecast/__pycache__/discovery.cpython-34.pyc,,
pychromecast/controllers/__pycache__/media.cpython-34.pyc,,
pychromecast/__pycache__/websocket.cpython-34.pyc,,

View File

@@ -0,0 +1,6 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.26.0)
Root-Is-Purelib: true
Tag: py2-none-any
Tag: py3-none-any

View File

@@ -0,0 +1 @@
{"generator": "bdist_wheel (0.26.0)", "summary": "Python module to talk to Google Chromecast.", "classifiers": ["Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", "Topic :: Software Development :: Libraries :: Python Modules"], "extensions": {"python.details": {"project_urls": {"Home": "https://github.com/balloob/pychromecast"}, "contacts": [{"email": "paulus@paulusschoutsen.nl", "name": "Paulus Schoutsen", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}}}, "license": "MIT", "metadata_version": "2.0", "name": "PyChromecast", "platform": "any", "run_requires": [{"requires": ["protobuf (==3.0.0b2)", "requests (>=2.0)", "six (>=1.10.0)", "zeroconf (>=0.17.4)"]}], "extras": [], "version": "0.7.4"}

View File

@@ -0,0 +1 @@
{"git_version": "03f56b3", "is_release": false}

View File

@@ -0,0 +1 @@
pychromecast

View File

@@ -0,0 +1,204 @@
This package provides a class to control the GPIO on a Raspberry Pi.
Note that this module is unsuitable for real-time or timing critical applications. This is because you
can not predict when Python will be busy garbage collecting. It also runs under the Linux kernel which
is not suitable for real time applications - it is multitasking O/S and another process may be given
priority over the CPU, causing jitter in your program. If you are after true real-time performance and
predictability, buy yourself an Arduino http://www.arduino.cc !
Note that the current release does not support SPI, I2C, hardware PWM or serial functionality on the RPi yet.
This is planned for the near future - watch this space! One-wire functionality is also planned.
Although hardware PWM is not available yet, software PWM is available to use on all channels.
For examples and documentation, visit http://sourceforge.net/p/raspberry-gpio-python/wiki/Home/
Change Log
==========
0.6.2
-----
- Rewrote Debian packaging mechanism
- RPI_INFO reports Pi 3
- Changed module layout - moved C components to RPi._GPIO
0.6.1
-----
- Update RPI_INFO to detect more board types
- Issue 118 - add_event_detect sometimes gives runtime error with unpriv user
- Issue 120 - setmode() remembers invalid mode
0.6.0a3
-------
- Now uses /dev/gpiomem if available to avoid being run as root
- Fix warnings with pull up/down on pins 3/5
- Correct base address on Pi 2 when devicetree is disabled
- caddr_t error on compile (Issue 109)
- Error on invalid parameters to setup() (issue 93)
- Add timeout parameter to wait_for_edge() (issue 91)
0.5.11
------
- Fix - pins > 26 missing when using BOARD mode
- Add getmode()
- Raise exception when a mix of modes is used
- GPIO.cleanaup() unsets the current pin mode
0.5.10
------
- Issue 95 - support RPi 2 boards
- Introduce RPI_INFO
- Deprecate RPI_REVISION
- Issue 97 - fixed docstring for setup()
0.5.9
-----
- Issue 87 - warn about pull up/down on i2c pins
- Issue 86/75 - wait_for_edge() bugfix
- Issue 84 - recognise RPi properly when using a custom kernel
- Issue 90 - cleanup() on a list/tuple of channels
0.5.8
-----
- Allow lists/tuples of channels in GPIO.setup()
- GPIO.output() now allows lists/tuples of values
- GPIO.wait_for_edge() bug fixes (issue 78)
0.5.7
-----
- Issue 67 - speed up repeated calls to GPIO.wait_for_event()
- Added bouncetime keyword to GPIO.wait_for_event()
- Added extra edge/interrupt unit tests
- GPIO.wait_for_event() can now be mixed with GPIO.add_event_detect()
- Improved cleanups of events
- Issue 69 resolved
0.5.6
-----
- Issue 68 - support for RPi Model B+
- Fix gpio_function()
0.5.5
-----
- Issue 52 - 'unallocate' a channel
- Issue 35 - use switchbounce with GPIO.event_detected()
- Refactored events code
- Rewrote tests to use unittest mechanism and new test board with loopbacks
- Fixed adding events after a GPIO.cleanup()
- Issue 64 - misleading /dev/mem permissions error
- Issue 59 - name collision with PWM constant and class
0.5.4
-----
- Changed release status (from alpha to full release)
- Warn when GPIO.cleanup() used with nothing to clean up (issue 44)
- Avoid collisions in constants (e.g. HIGH / RISING / PUD_DOWN)
- Accept BOARD numbers in gpio_function (issue 34)
- More return values for gpio_function (INPUT, OUTPUT, SPI, I2C, PWM, SERIAL, UNKNOWN)
- Tidy up docstrings
- Fix /dev/mem access error with gpio_function
0.5.3a
------
- Allow pydoc for non-root users (issue 27)
- Fix add_event_detect error when run as daemon (issue 32)
- Simplified exception types
- Changed from distribute to pip
0.5.2a
------
- Added software PWM (experimental)
- Added switch bounce handling to event callbacks
- Added channel number parameter to event callbacks (issue 31)
- Internal refactoring and code tidy up
0.5.1a
------
- Fixed callbacks for multiple GPIOs (issue 28)
0.5.0a
------
- Added new edge detection events (interrupt handling)
- Added add_event_detect()
- Added remove_event_detect()
- Added add_event_callback()
- Added wait_for_edge()
- Removed old experimental event functions
- Removed set_rising_event()
- Removed set_falling_event()
- Removed set_high_event()
- Removed set_low_event()
- Changed event_detected() for new edge detection functionality
- input() now returns 0/LOW == False or 1/HIGH == True (integers) instead of False or True (booleans).
- Fix error on repeated import (issue 3)
- Change SetupException to a RuntimeError so it can be caught on import (issue 25, Chris Hager <chris@linuxuser.at>)
- Improved docstrings of functions
0.4.2a
------
- Fix for installing on Arch Linux (Python 3.3) (issue 20)
- Initial value when setting a channel as an output (issue 19)
0.4.1a
------
- Added VERSION
- Permit input() of channels set as outputs (Eric Ptak <trouch@trouch.com>)
0.4.0a
------
- Added support for Revision 2 boards
- Added RPI_REVISION
- Added cleanup() function and removed automatic reset functionality on program exit
- Added get_function() to read existing GPIO channel functionality (suggestion from Eric Ptak <trouch@trouch.com>)
- Added set_rising_event()
- Added set_falling_event()
- Added set_high_event()
- Added set_low_event()
- Added event_detected()
- Added test/test.py
- Converted debian to armhf
- Fixed C function short_wait() (thanks to Thibault Porteboeuf <thibaultporteboeuf@gmail.com>)
0.3.1a
------
- Fixed critical bug with swapped high/low state on outputs
- Added pull-up / pull-down setup functionality for inputs
0.3.0a
------
- Rewritten as a C extension
- Now uses /dev/mem and SoC registers instead of /sys/class/gpio
- Faster!
- Make call to GPIO.setmode() mandatory
- Added GPIO.HIGH and GPIO.LOW constants
0.2.0
-----
- Changed status from alpha to beta
- Added setmode() to be able to use BCM GPIO 00.nn channel numbers
- Renamed InvalidPinException to InvalidChannelException
0.1.0
------
- Fixed direction bug
- Added MANIFEST.in (to include missing file)
- Changed GPIO channel number to pin number
- Tested and working!
0.0.3a
------
- Added GPIO table
- Refactored
- Fixed a few critical bugs
- Still completely untested!
0.0.2a
------
- Internal refactoring. Still completely untested!
0.0.1a
------
- First version. Completely untested until I can get hold of a Raspberry Pi!

View File

@@ -0,0 +1 @@
pip

224
deps/RPi.GPIO-0.6.2.dist-info/METADATA vendored Normal file
View File

@@ -0,0 +1,224 @@
Metadata-Version: 2.0
Name: RPi.GPIO
Version: 0.6.2
Summary: A module to control Raspberry Pi GPIO channels
Home-page: http://sourceforge.net/projects/raspberry-gpio-python/
Author: Ben Croston
Author-email: ben@croston.org
License: MIT
Keywords: Raspberry Pi GPIO
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Operating System :: POSIX :: Linux
Classifier: License :: OSI Approved :: MIT License
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development
Classifier: Topic :: Home Automation
Classifier: Topic :: System :: Hardware
This package provides a class to control the GPIO on a Raspberry Pi.
Note that this module is unsuitable for real-time or timing critical applications. This is because you
can not predict when Python will be busy garbage collecting. It also runs under the Linux kernel which
is not suitable for real time applications - it is multitasking O/S and another process may be given
priority over the CPU, causing jitter in your program. If you are after true real-time performance and
predictability, buy yourself an Arduino http://www.arduino.cc !
Note that the current release does not support SPI, I2C, hardware PWM or serial functionality on the RPi yet.
This is planned for the near future - watch this space! One-wire functionality is also planned.
Although hardware PWM is not available yet, software PWM is available to use on all channels.
For examples and documentation, visit http://sourceforge.net/p/raspberry-gpio-python/wiki/Home/
Change Log
==========
0.6.2
-----
- Rewrote Debian packaging mechanism
- RPI_INFO reports Pi 3
- Changed module layout - moved C components to RPi._GPIO
0.6.1
-----
- Update RPI_INFO to detect more board types
- Issue 118 - add_event_detect sometimes gives runtime error with unpriv user
- Issue 120 - setmode() remembers invalid mode
0.6.0a3
-------
- Now uses /dev/gpiomem if available to avoid being run as root
- Fix warnings with pull up/down on pins 3/5
- Correct base address on Pi 2 when devicetree is disabled
- caddr_t error on compile (Issue 109)
- Error on invalid parameters to setup() (issue 93)
- Add timeout parameter to wait_for_edge() (issue 91)
0.5.11
------
- Fix - pins > 26 missing when using BOARD mode
- Add getmode()
- Raise exception when a mix of modes is used
- GPIO.cleanaup() unsets the current pin mode
0.5.10
------
- Issue 95 - support RPi 2 boards
- Introduce RPI_INFO
- Deprecate RPI_REVISION
- Issue 97 - fixed docstring for setup()
0.5.9
-----
- Issue 87 - warn about pull up/down on i2c pins
- Issue 86/75 - wait_for_edge() bugfix
- Issue 84 - recognise RPi properly when using a custom kernel
- Issue 90 - cleanup() on a list/tuple of channels
0.5.8
-----
- Allow lists/tuples of channels in GPIO.setup()
- GPIO.output() now allows lists/tuples of values
- GPIO.wait_for_edge() bug fixes (issue 78)
0.5.7
-----
- Issue 67 - speed up repeated calls to GPIO.wait_for_event()
- Added bouncetime keyword to GPIO.wait_for_event()
- Added extra edge/interrupt unit tests
- GPIO.wait_for_event() can now be mixed with GPIO.add_event_detect()
- Improved cleanups of events
- Issue 69 resolved
0.5.6
-----
- Issue 68 - support for RPi Model B+
- Fix gpio_function()
0.5.5
-----
- Issue 52 - 'unallocate' a channel
- Issue 35 - use switchbounce with GPIO.event_detected()
- Refactored events code
- Rewrote tests to use unittest mechanism and new test board with loopbacks
- Fixed adding events after a GPIO.cleanup()
- Issue 64 - misleading /dev/mem permissions error
- Issue 59 - name collision with PWM constant and class
0.5.4
-----
- Changed release status (from alpha to full release)
- Warn when GPIO.cleanup() used with nothing to clean up (issue 44)
- Avoid collisions in constants (e.g. HIGH / RISING / PUD_DOWN)
- Accept BOARD numbers in gpio_function (issue 34)
- More return values for gpio_function (INPUT, OUTPUT, SPI, I2C, PWM, SERIAL, UNKNOWN)
- Tidy up docstrings
- Fix /dev/mem access error with gpio_function
0.5.3a
------
- Allow pydoc for non-root users (issue 27)
- Fix add_event_detect error when run as daemon (issue 32)
- Simplified exception types
- Changed from distribute to pip
0.5.2a
------
- Added software PWM (experimental)
- Added switch bounce handling to event callbacks
- Added channel number parameter to event callbacks (issue 31)
- Internal refactoring and code tidy up
0.5.1a
------
- Fixed callbacks for multiple GPIOs (issue 28)
0.5.0a
------
- Added new edge detection events (interrupt handling)
- Added add_event_detect()
- Added remove_event_detect()
- Added add_event_callback()
- Added wait_for_edge()
- Removed old experimental event functions
- Removed set_rising_event()
- Removed set_falling_event()
- Removed set_high_event()
- Removed set_low_event()
- Changed event_detected() for new edge detection functionality
- input() now returns 0/LOW == False or 1/HIGH == True (integers) instead of False or True (booleans).
- Fix error on repeated import (issue 3)
- Change SetupException to a RuntimeError so it can be caught on import (issue 25, Chris Hager <chris@linuxuser.at>)
- Improved docstrings of functions
0.4.2a
------
- Fix for installing on Arch Linux (Python 3.3) (issue 20)
- Initial value when setting a channel as an output (issue 19)
0.4.1a
------
- Added VERSION
- Permit input() of channels set as outputs (Eric Ptak <trouch@trouch.com>)
0.4.0a
------
- Added support for Revision 2 boards
- Added RPI_REVISION
- Added cleanup() function and removed automatic reset functionality on program exit
- Added get_function() to read existing GPIO channel functionality (suggestion from Eric Ptak <trouch@trouch.com>)
- Added set_rising_event()
- Added set_falling_event()
- Added set_high_event()
- Added set_low_event()
- Added event_detected()
- Added test/test.py
- Converted debian to armhf
- Fixed C function short_wait() (thanks to Thibault Porteboeuf <thibaultporteboeuf@gmail.com>)
0.3.1a
------
- Fixed critical bug with swapped high/low state on outputs
- Added pull-up / pull-down setup functionality for inputs
0.3.0a
------
- Rewritten as a C extension
- Now uses /dev/mem and SoC registers instead of /sys/class/gpio
- Faster!
- Make call to GPIO.setmode() mandatory
- Added GPIO.HIGH and GPIO.LOW constants
0.2.0
-----
- Changed status from alpha to beta
- Added setmode() to be able to use BCM GPIO 00.nn channel numbers
- Renamed InvalidPinException to InvalidChannelException
0.1.0
------
- Fixed direction bug
- Added MANIFEST.in (to include missing file)
- Changed GPIO channel number to pin number
- Tested and working!
0.0.3a
------
- Added GPIO table
- Refactored
- Fixed a few critical bugs
- Still completely untested!
0.0.2a
------
- Internal refactoring. Still completely untested!
0.0.1a
------
- First version. Completely untested until I can get hold of a Raspberry Pi!

12
deps/RPi.GPIO-0.6.2.dist-info/RECORD vendored Normal file
View File

@@ -0,0 +1,12 @@
RPi/_GPIO.cpython-34m.so,sha256=vAgqvC2ElrF2cSyFgxJ_SvDLSJ6m5h6R77GxoBQ9nDg,132144
RPi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
RPi/GPIO/__init__.py,sha256=jnxwfrIfmsuc4CHYCyD7fhmXuK910T8nJZly3TFbZ1s,1112
RPi.GPIO-0.6.2.dist-info/DESCRIPTION.rst,sha256=XQZ6vEQCpIDCQyetosBFH2erMMSGGq-K6mG1Lw99Yrw,6289
RPi.GPIO-0.6.2.dist-info/METADATA,sha256=_m7PpAOt0icvoUp0OCBMJRRu-HCWgyoWnSg-4Bnn9Jg,6988
RPi.GPIO-0.6.2.dist-info/RECORD,,
RPi.GPIO-0.6.2.dist-info/WHEEL,sha256=qlXKyUxaKdKGVZKHdQaMFOPNWg5qC8OVyb-aicJwJ6U,104
RPi.GPIO-0.6.2.dist-info/metadata.json,sha256=tQq4Ev0lJJDCwtSI_mlRDqSWLcCT9vEVAO9VKv7fg8Y,834
RPi.GPIO-0.6.2.dist-info/top_level.txt,sha256=D2ebmx5QNuKCb-J2LbVMXkkhIVNpXvmxlOVIZjPsALw,4
RPi.GPIO-0.6.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
RPi/GPIO/__pycache__/__init__.cpython-34.pyc,,
RPi/__pycache__/__init__.cpython-34.pyc,,

5
deps/RPi.GPIO-0.6.2.dist-info/WHEEL vendored Normal file
View File

@@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.29.0)
Root-Is-Purelib: false
Tag: cp34-cp34m-linux_armv7l

View File

@@ -0,0 +1 @@
{"classifiers": ["Development Status :: 5 - Production/Stable", "Operating System :: POSIX :: Linux", "License :: OSI Approved :: MIT License", "Intended Audience :: Developers", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Topic :: Software Development", "Topic :: Home Automation", "Topic :: System :: Hardware"], "extensions": {"python.details": {"contacts": [{"email": "ben@croston.org", "name": "Ben Croston", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://sourceforge.net/projects/raspberry-gpio-python/"}}}, "generator": "bdist_wheel (0.29.0)", "keywords": ["Raspberry", "Pi", "GPIO"], "license": "MIT", "metadata_version": "2.0", "name": "RPi.GPIO", "summary": "A module to control Raspberry Pi GPIO channels", "version": "0.6.2"}

View File

@@ -0,0 +1 @@
RPi

25
deps/RPi/GPIO/__init__.py vendored Normal file
View File

@@ -0,0 +1,25 @@
"""
Copyright (c) 2012-2016 Ben Croston
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.
"""
from RPi._GPIO import *
VERSION = '0.6.2'

Binary file not shown.

BIN
deps/RPi/_GPIO.cpython-34m.so vendored Executable file

Binary file not shown.

0
deps/RPi/__init__.py vendored Normal file
View File

Binary file not shown.

View File

@@ -0,0 +1,137 @@
SQLAlchemy
==========
The Python SQL Toolkit and Object Relational Mapper
Introduction
-------------
SQLAlchemy is the Python SQL toolkit and Object Relational Mapper
that gives application developers the full power and
flexibility of SQL. SQLAlchemy provides a full suite
of well known enterprise-level persistence patterns,
designed for efficient and high-performing database
access, adapted into a simple and Pythonic domain
language.
Major SQLAlchemy features include:
* An industrial strength ORM, built
from the core on the identity map, unit of work,
and data mapper patterns. These patterns
allow transparent persistence of objects
using a declarative configuration system.
Domain models
can be constructed and manipulated naturally,
and changes are synchronized with the
current transaction automatically.
* A relationally-oriented query system, exposing
the full range of SQL's capabilities
explicitly, including joins, subqueries,
correlation, and most everything else,
in terms of the object model.
Writing queries with the ORM uses the same
techniques of relational composition you use
when writing SQL. While you can drop into
literal SQL at any time, it's virtually never
needed.
* A comprehensive and flexible system
of eager loading for related collections and objects.
Collections are cached within a session,
and can be loaded on individual access, all
at once using joins, or by query per collection
across the full result set.
* A Core SQL construction system and DBAPI
interaction layer. The SQLAlchemy Core is
separate from the ORM and is a full database
abstraction layer in its own right, and includes
an extensible Python-based SQL expression
language, schema metadata, connection pooling,
type coercion, and custom types.
* All primary and foreign key constraints are
assumed to be composite and natural. Surrogate
integer primary keys are of course still the
norm, but SQLAlchemy never assumes or hardcodes
to this model.
* Database introspection and generation. Database
schemas can be "reflected" in one step into
Python structures representing database metadata;
those same structures can then generate
CREATE statements right back out - all within
the Core, independent of the ORM.
SQLAlchemy's philosophy:
* SQL databases behave less and less like object
collections the more size and performance start to
matter; object collections behave less and less like
tables and rows the more abstraction starts to matter.
SQLAlchemy aims to accommodate both of these
principles.
* An ORM doesn't need to hide the "R". A relational
database provides rich, set-based functionality
that should be fully exposed. SQLAlchemy's
ORM provides an open-ended set of patterns
that allow a developer to construct a custom
mediation layer between a domain model and
a relational schema, turning the so-called
"object relational impedance" issue into
a distant memory.
* The developer, in all cases, makes all decisions
regarding the design, structure, and naming conventions
of both the object model as well as the relational
schema. SQLAlchemy only provides the means
to automate the execution of these decisions.
* With SQLAlchemy, there's no such thing as
"the ORM generated a bad query" - you
retain full control over the structure of
queries, including how joins are organized,
how subqueries and correlation is used, what
columns are requested. Everything SQLAlchemy
does is ultimately the result of a developer-
initiated decision.
* Don't use an ORM if the problem doesn't need one.
SQLAlchemy consists of a Core and separate ORM
component. The Core offers a full SQL expression
language that allows Pythonic construction
of SQL constructs that render directly to SQL
strings for a target database, returning
result sets that are essentially enhanced DBAPI
cursors.
* Transactions should be the norm. With SQLAlchemy's
ORM, nothing goes to permanent storage until
commit() is called. SQLAlchemy encourages applications
to create a consistent means of delineating
the start and end of a series of operations.
* Never render a literal value in a SQL statement.
Bound parameters are used to the greatest degree
possible, allowing query optimizers to cache
query plans effectively and making SQL injection
attacks a non-issue.
Documentation
-------------
Latest documentation is at:
http://www.sqlalchemy.org/docs/
Installation / Requirements
---------------------------
Full documentation for installation is at
`Installation <http://www.sqlalchemy.org/docs/intro.html#installation>`_.
Getting Help / Development / Bug reporting
------------------------------------------
Please refer to the `SQLAlchemy Community Guide <http://www.sqlalchemy.org/support.html>`_.
License
-------
SQLAlchemy is distributed under the `MIT license
<http://www.opensource.org/licenses/mit-license.php>`_.

View File

@@ -0,0 +1 @@
pip

View File

@@ -0,0 +1,157 @@
Metadata-Version: 2.0
Name: SQLAlchemy
Version: 1.0.15
Summary: Database Abstraction Library
Home-page: http://www.sqlalchemy.org
Author: Mike Bayer
Author-email: mike_mp@zzzcomputing.com
License: MIT License
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: Jython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Database :: Front-Ends
Classifier: Operating System :: OS Independent
SQLAlchemy
==========
The Python SQL Toolkit and Object Relational Mapper
Introduction
-------------
SQLAlchemy is the Python SQL toolkit and Object Relational Mapper
that gives application developers the full power and
flexibility of SQL. SQLAlchemy provides a full suite
of well known enterprise-level persistence patterns,
designed for efficient and high-performing database
access, adapted into a simple and Pythonic domain
language.
Major SQLAlchemy features include:
* An industrial strength ORM, built
from the core on the identity map, unit of work,
and data mapper patterns. These patterns
allow transparent persistence of objects
using a declarative configuration system.
Domain models
can be constructed and manipulated naturally,
and changes are synchronized with the
current transaction automatically.
* A relationally-oriented query system, exposing
the full range of SQL's capabilities
explicitly, including joins, subqueries,
correlation, and most everything else,
in terms of the object model.
Writing queries with the ORM uses the same
techniques of relational composition you use
when writing SQL. While you can drop into
literal SQL at any time, it's virtually never
needed.
* A comprehensive and flexible system
of eager loading for related collections and objects.
Collections are cached within a session,
and can be loaded on individual access, all
at once using joins, or by query per collection
across the full result set.
* A Core SQL construction system and DBAPI
interaction layer. The SQLAlchemy Core is
separate from the ORM and is a full database
abstraction layer in its own right, and includes
an extensible Python-based SQL expression
language, schema metadata, connection pooling,
type coercion, and custom types.
* All primary and foreign key constraints are
assumed to be composite and natural. Surrogate
integer primary keys are of course still the
norm, but SQLAlchemy never assumes or hardcodes
to this model.
* Database introspection and generation. Database
schemas can be "reflected" in one step into
Python structures representing database metadata;
those same structures can then generate
CREATE statements right back out - all within
the Core, independent of the ORM.
SQLAlchemy's philosophy:
* SQL databases behave less and less like object
collections the more size and performance start to
matter; object collections behave less and less like
tables and rows the more abstraction starts to matter.
SQLAlchemy aims to accommodate both of these
principles.
* An ORM doesn't need to hide the "R". A relational
database provides rich, set-based functionality
that should be fully exposed. SQLAlchemy's
ORM provides an open-ended set of patterns
that allow a developer to construct a custom
mediation layer between a domain model and
a relational schema, turning the so-called
"object relational impedance" issue into
a distant memory.
* The developer, in all cases, makes all decisions
regarding the design, structure, and naming conventions
of both the object model as well as the relational
schema. SQLAlchemy only provides the means
to automate the execution of these decisions.
* With SQLAlchemy, there's no such thing as
"the ORM generated a bad query" - you
retain full control over the structure of
queries, including how joins are organized,
how subqueries and correlation is used, what
columns are requested. Everything SQLAlchemy
does is ultimately the result of a developer-
initiated decision.
* Don't use an ORM if the problem doesn't need one.
SQLAlchemy consists of a Core and separate ORM
component. The Core offers a full SQL expression
language that allows Pythonic construction
of SQL constructs that render directly to SQL
strings for a target database, returning
result sets that are essentially enhanced DBAPI
cursors.
* Transactions should be the norm. With SQLAlchemy's
ORM, nothing goes to permanent storage until
commit() is called. SQLAlchemy encourages applications
to create a consistent means of delineating
the start and end of a series of operations.
* Never render a literal value in a SQL statement.
Bound parameters are used to the greatest degree
possible, allowing query optimizers to cache
query plans effectively and making SQL injection
attacks a non-issue.
Documentation
-------------
Latest documentation is at:
http://www.sqlalchemy.org/docs/
Installation / Requirements
---------------------------
Full documentation for installation is at
`Installation <http://www.sqlalchemy.org/docs/intro.html#installation>`_.
Getting Help / Development / Bug reporting
------------------------------------------
Please refer to the `SQLAlchemy Community Guide <http://www.sqlalchemy.org/support.html>`_.
License
-------
SQLAlchemy is distributed under the `MIT license
<http://www.opensource.org/licenses/mit-license.php>`_.

376
deps/SQLAlchemy-1.0.15.dist-info/RECORD vendored Normal file
View File

@@ -0,0 +1,376 @@
SQLAlchemy-1.0.15.dist-info/DESCRIPTION.rst,sha256=ZN8fj2owI_rw0Emr3_RXqoNfTFkThjiZy7xcCzg1W_g,5013
SQLAlchemy-1.0.15.dist-info/METADATA,sha256=PXrsCp9uV7PWUcEVkGZfGxqVJ9hPqrmf4bF01OegS74,5786
SQLAlchemy-1.0.15.dist-info/RECORD,,
SQLAlchemy-1.0.15.dist-info/WHEEL,sha256=qlXKyUxaKdKGVZKHdQaMFOPNWg5qC8OVyb-aicJwJ6U,104
SQLAlchemy-1.0.15.dist-info/metadata.json,sha256=6ZjUYl1zEtn3LvCDChujq-h7i3ttPnXQ-Qa0ZTVQ3Ps,965
SQLAlchemy-1.0.15.dist-info/top_level.txt,sha256=rp-ZgB7D8G11ivXON5VGPjupT1voYmWqkciDt5Uaw_Q,11
sqlalchemy/__init__.py,sha256=TXDtb6mvt-pw89t8oryfyzN1O_-tLB008zsHbspt6A0,2129
sqlalchemy/cprocessors.cpython-34m.so,sha256=il80Q4miLp0Jrhy98MHAXTV8TATdlZVuY9GuoKo_fzQ,34448
sqlalchemy/cresultproxy.cpython-34m.so,sha256=WtHt8ezIDtS12UIvsrMDudamGPk4AMdc10HXsXLcItU,42664
sqlalchemy/cutils.cpython-34m.so,sha256=UAduuJiZ7iABA3VMMcdmp-FqqgIm-XmbjgZhtITrrzI,19276
sqlalchemy/events.py,sha256=4gPVD8hBxWEUrzMpGiNcmDrpCMRaSqvps04offK-fVo,43942
sqlalchemy/exc.py,sha256=NhA5R5nDdducWkp0MXtlQ0-Q6iF_rhqkHWblIfuSYGk,11706
sqlalchemy/inspection.py,sha256=zMa-2nt-OQ0Op1dqq0Z2XCnpdAFSTkqif5Kdi8Wz8AU,3093
sqlalchemy/interfaces.py,sha256=XSx5y-HittAzc79lU4C7rPbTtSW_Hc2c89NqCy50tsQ,10967
sqlalchemy/log.py,sha256=opX7UORq5N6_jWxN9aHX9OpiirwAcRA0qq-u5m4SMkQ,6712
sqlalchemy/pool.py,sha256=-F51TIJYl0XGTV2_sdpV8C1m0jTTQaq0nAezdmSgr84,47220
sqlalchemy/processors.py,sha256=Li1kdC-I0v03JxeOz4V7u4HAevK6LledyCPvaL06mYc,5220
sqlalchemy/schema.py,sha256=af2xp2pyTsbPLrSIBr4aOjc25bjnqku_uXL9pqEltx8,1200
sqlalchemy/types.py,sha256=qcoy5xKaurDV4kaXr489GL2sz8FKkWX21Us3ZCqeasg,1650
sqlalchemy/connectors/__init__.py,sha256=97YbriYu5mcljh7opc1JOScRlf3Tk8ldbn5urBVm4WY,278
sqlalchemy/connectors/mxodbc.py,sha256=4giS6L3cIe3JBtTAg1pZpxuIJKd7xV1Cq7YUf5Mea-8,5347
sqlalchemy/connectors/pyodbc.py,sha256=pG2yf3cEDtTr-w_m4to6jF5l8hZk6MJv69K3cg84NfY,6264
sqlalchemy/connectors/zxJDBC.py,sha256=2KK_sVSgMsdW0ufZqAwgXjd1FsMb4hqbiUQRAkM0RYg,1868
sqlalchemy/databases/__init__.py,sha256=BaQyAuMjXNpZYV47hCseHrDtPzTfSw-iqUQYxMWJddw,817
sqlalchemy/dialects/__init__.py,sha256=7SMul8PL3gkbJRUwAwovHLae5qBBApRF-VcRwU-VtdU,1012
sqlalchemy/dialects/postgres.py,sha256=heNVHys6E91DIBepXT3ls_4_6N8HTTahrZ49W5IR3M0,614
sqlalchemy/dialects/firebird/__init__.py,sha256=QYmQ0SaGfq3YjDraCV9ALwqVW5A3KDUF0F6air_qp3Q,664
sqlalchemy/dialects/firebird/base.py,sha256=IT0prWkh1TFSTke-BqGdVMGdof53zmWWk6zbJZ_TuuI,28170
sqlalchemy/dialects/firebird/fdb.py,sha256=l4s6_8Z0HvqxgqGz0LNcKWP1qUmEc3M2XM718_drN34,4325
sqlalchemy/dialects/firebird/kinterbasdb.py,sha256=kCsn2ed4u9fyjcyfEI3rXQdKvL05z9wtf5YjW9-NrvI,6299
sqlalchemy/dialects/mssql/__init__.py,sha256=G12xmirGZgMzfUKZCA8BFfaCmqUDuYca9Fu2VP_eaks,1081
sqlalchemy/dialects/mssql/adodbapi.py,sha256=dHZgS3pEDX39ixhlDfTtDcjCq6rdjF85VS7rIZ1TfYo,2493
sqlalchemy/dialects/mssql/base.py,sha256=FQ9DNxTBqnFInTNmffcmZeySpj-_53l_aQFxNWB2ljY,68991
sqlalchemy/dialects/mssql/information_schema.py,sha256=pwuTsgOCY5eSBW9w-g-pyJDRfyuZ_rOEXXNYRuAroCE,6418
sqlalchemy/dialects/mssql/mxodbc.py,sha256=G9LypIeEizgxeShtDu2M7Vwm8NopnzaTmnZMD49mYeg,3856
sqlalchemy/dialects/mssql/pymssql.py,sha256=fQE2el6WDwm8EeFqNn9qYXyw_oFPFqidA2zd1fXs8G0,3080
sqlalchemy/dialects/mssql/pyodbc.py,sha256=LAamdDoPAMSTa0I-51PlJ_sVvyM5M4f99XQcz9mMZR8,9653
sqlalchemy/dialects/mssql/zxjdbc.py,sha256=u4uBgwk0LbI7_I5CIvM3C4bBb0pmrw2_DqRh_ehJTkI,2282
sqlalchemy/dialects/mysql/__init__.py,sha256=3cQ2juPT8LsZTicPa2J-0rCQjQIQaPgyBzxjV3O_7xs,1171
sqlalchemy/dialects/mysql/base.py,sha256=HZA1lxTNHYRVxeMeAw8RwJz2lYbRRYY1qJ5J_SujC9Q,123316
sqlalchemy/dialects/mysql/cymysql.py,sha256=nqsdQA8LBLIc6eilgX6qwkjm7szsUoqMTVYwK9kkfsE,2349
sqlalchemy/dialects/mysql/gaerdbms.py,sha256=2MxtTsIqlpq_J32HHqDzz-5vu-mC51Lb7PvyGkJa73M,3387
sqlalchemy/dialects/mysql/mysqlconnector.py,sha256=K7HvQzLUZGm_pIL2ppltfjGIHPsVlr6R-F50dMg2V7g,6106
sqlalchemy/dialects/mysql/mysqldb.py,sha256=McqROngxAknbLOXoUAG9o9mP9FQBLs-ouD-JqqI2Ses,6564
sqlalchemy/dialects/mysql/oursql.py,sha256=rmdr-r66iJ2amqFeGvCohvE8WCl_i6R9KcgVG0uXOQs,8124
sqlalchemy/dialects/mysql/pymysql.py,sha256=e-qehI-sASmAjEa0ajHqjZjlyJYWsb3RPQY4iBR5pz0,1504
sqlalchemy/dialects/mysql/pyodbc.py,sha256=Ze9IOKw6ANVQj25IlmSGR8aaJhM0pMuRtbzKF7UsZCY,2665
sqlalchemy/dialects/mysql/zxjdbc.py,sha256=LIhe2mHSRVgi8I7qmiTMVBRSpuWJVnuDtpHTUivIx0M,3942
sqlalchemy/dialects/oracle/__init__.py,sha256=UhF2ZyPfT3EFAnP8ZjGng6GnWSzmAkjMax0Lucpn0Bg,797
sqlalchemy/dialects/oracle/base.py,sha256=9EnyRy5oRf528Njj1dHohB8_1Gh3FivcLcSovH8w7Q0,56790
sqlalchemy/dialects/oracle/cx_oracle.py,sha256=rQPBYvlS0KZIcw4Pg1ARlGKdmOUcR0xsGge0CXVhxfs,38765
sqlalchemy/dialects/oracle/zxjdbc.py,sha256=nC7XOCY3NdTLrEyIacNTnLDCaeVjWn59q8UYssJL8Wo,8112
sqlalchemy/dialects/postgresql/__init__.py,sha256=SjCtM5b3EaGyRaTyg_i82sh_qjkLEIVUXW91XDihiCM,1299
sqlalchemy/dialects/postgresql/base.py,sha256=SJ_gCtLwlrhR-h02Vi4dn5bNI1eHdZXTZKf1nBwXJ8g,104340
sqlalchemy/dialects/postgresql/constraints.py,sha256=8UDx_2TNQgqIUSRETZPhgninJigQ6rMfdRNI6vIt3Is,3119
sqlalchemy/dialects/postgresql/hstore.py,sha256=n8Wsd7Uldk3bbg66tTa0NKjVqjhJUbF1mVeUsM7keXA,11402
sqlalchemy/dialects/postgresql/json.py,sha256=MTlIGinMDa8iaVbZMOzYnremo0xL4tn2wyGTPwnvX6U,12215
sqlalchemy/dialects/postgresql/pg8000.py,sha256=x6o3P8Ad0wKsuF9qeyip39BKc5ORJZ4nWxv-8qOdj0E,8375
sqlalchemy/dialects/postgresql/psycopg2.py,sha256=Z6ubvg7bzVBBiyTebyvf1WGX4MgJlryRHHzyLQp3qEU,27019
sqlalchemy/dialects/postgresql/psycopg2cffi.py,sha256=8R3POkJH8z8a2DxwKNmfmQOsxFqsg4tU_OnjGj3OfDA,1651
sqlalchemy/dialects/postgresql/pypostgresql.py,sha256=raQRfZb8T9-c-jmq1w86Wci5QyiXgf_9_71OInT_sAw,2655
sqlalchemy/dialects/postgresql/ranges.py,sha256=MihdGXMdmCM6ToIlrj7OJx9Qh_8BX8bv5PSaAepHmII,4814
sqlalchemy/dialects/postgresql/zxjdbc.py,sha256=AhEGRiAy8q-GM0BStFcsLBgSwjxHkkwy2-BSroIoADo,1397
sqlalchemy/dialects/sqlite/__init__.py,sha256=0wW0VOhE_RtFDpRcbwvvo3XtD6Y2-SDgG4K7468eh_w,736
sqlalchemy/dialects/sqlite/base.py,sha256=_L9-854ITf8Fl2BgUymF9fKjDFvXSo7Pb2yuz1CMkDo,55007
sqlalchemy/dialects/sqlite/pysqlcipher.py,sha256=sgXCqn8ZtNIeTDwyo253Kj5mn4TPlIW3AZCNNmURi2A,4129
sqlalchemy/dialects/sqlite/pysqlite.py,sha256=G-Cg-iI-ErYsVjOH4UlQTEY9pLnLOLV89ik8q0-reuY,14980
sqlalchemy/dialects/sybase/__init__.py,sha256=gwCgFR_C_hoj0Re7PiaW3zmKSWaLpsd96UVXdM7EnTM,894
sqlalchemy/dialects/sybase/base.py,sha256=Xpl3vEd5VDyvoIRMg0DZa48Or--yBSrhaZ2CbTSCt0w,28853
sqlalchemy/dialects/sybase/mxodbc.py,sha256=E_ask6yFSjyhNPvv7gQsvA41WmyxbBvRGWjCyPVr9Gs,901
sqlalchemy/dialects/sybase/pyodbc.py,sha256=0a_gKwrIweJGcz3ZRYuQZb5BIvwjGmFEYBo9wGk66kI,2102
sqlalchemy/dialects/sybase/pysybase.py,sha256=tu2V_EbtgxWYOvt-ybo5_lLiBQzsIFaAtF8e7S1_-rk,3208
sqlalchemy/engine/__init__.py,sha256=orab-ubkvGHzmhExRx2e6zg1hvNOiF1AU-i48xMqcvc,18837
sqlalchemy/engine/base.py,sha256=HryAJKI1-jeViL8WVNFSjvBWfT9G20wUFRpcybW373s,79395
sqlalchemy/engine/default.py,sha256=U_yaliCazUHp6cfk_NVzhB4F_zOJSyy959rHyk40J4M,36548
sqlalchemy/engine/interfaces.py,sha256=CmPYM_oDp1zAPH13sKmufO4Tuha6KA-fXRQq-K_3YTE,35908
sqlalchemy/engine/reflection.py,sha256=jk5_oCt6oFbwznVTGXkhgj7L9xUziL1cMsuvTLyafUA,28653
sqlalchemy/engine/result.py,sha256=Qh8w8mbOPcXPpVoMfwLkBts0gT-_6LASYsCZ6BuvnJA,44360
sqlalchemy/engine/strategies.py,sha256=mwy-CTrnXzyaIA1TRQBQ_Z2O8wN0lnTNZwDefEWCR9A,8929
sqlalchemy/engine/threadlocal.py,sha256=y4wOLjtbeY-dvp2GcJDtos6F2jzfP11JVAaSFwZ0zRM,4191
sqlalchemy/engine/url.py,sha256=ZhS_Iqiu6V1kfIM2pcv3ud9fOPXkFOHBv8wiLOqbJhc,8228
sqlalchemy/engine/util.py,sha256=Tvb9sIkyd6qOwIA-RsBmo5j877UXa5x-jQmhqnhHWRA,2338
sqlalchemy/event/__init__.py,sha256=KnUVp-NVX6k276ntGffxgkjVmIWR22FSlzrbAKqQ6S4,419
sqlalchemy/event/api.py,sha256=O2udbj5D7HdXcvsGBQk6-dK9CAFfePTypWOrUdqmhYY,5990
sqlalchemy/event/attr.py,sha256=VfRJJl4RD24mQaIoDwArWL2hsGOX6ISSU6vKusVMNO0,12053
sqlalchemy/event/base.py,sha256=DWDKZV19fFsLavu2cXOxXV8NhO3XuCbKcKamBKyXuME,9540
sqlalchemy/event/legacy.py,sha256=ACnVeBUt8uwVfh1GNRu22cWCADC3CWZdrsBKzAd6UQQ,5814
sqlalchemy/event/registry.py,sha256=13wx1qdEmcQeCoAmgf_WQEMuR43h3v7iyd2Re54QdOE,7786
sqlalchemy/ext/__init__.py,sha256=smCZIGgjJprT4ddhuYSLZ8PrTn4NdXPP3j03a038SdE,322
sqlalchemy/ext/associationproxy.py,sha256=y61Y4UIZNBit5lqk2WzdHTCXIWRrBg3hHbRVsqXjnqE,33422
sqlalchemy/ext/automap.py,sha256=JXZ4SgtnU0SuNMxs9iwWpBf1WAd44HDS0u1skn3wWKI,41566
sqlalchemy/ext/baked.py,sha256=1ny-NbAxFbg82U8RxJLMqZGg-yBP7I5cOIZkO36XsX0,17000
sqlalchemy/ext/compiler.py,sha256=aSSlySoTsqN-JkACWFIhv3pq2CuZwxKm6pSDfQoc10Q,16257
sqlalchemy/ext/horizontal_shard.py,sha256=XEBYIfs0YrTt_2vRuaBY6C33ZOZMUHQb2E4X2s3Szns,4814
sqlalchemy/ext/hybrid.py,sha256=wNXvuYEEmKy-Nc6z7fu1c2gNWCMOiQA0N14Y3FCq5lo,27989
sqlalchemy/ext/instrumentation.py,sha256=HRgNiuYJ90_uSKC1iDwsEl8_KXscMQkEb9KeElk-yLE,14856
sqlalchemy/ext/mutable.py,sha256=QvIb2-tLqgR3Xdq94y8zJqX9cxfIUgJ5K7aDV6HIlww,25440
sqlalchemy/ext/orderinglist.py,sha256=UCkuZxTWAQ0num-b5oNm8zNJAmVuIFcbFXt5e7JPx-U,13816
sqlalchemy/ext/serializer.py,sha256=fK3N1miYF16PSIZDjLFS2zI7y-scZ9qtmopXIfzPqrA,5586
sqlalchemy/ext/declarative/__init__.py,sha256=Jpwf2EukqwNe4RzDfCmX1p-hQ6pPhJEIL_xunaER3tw,756
sqlalchemy/ext/declarative/api.py,sha256=SHjIAi5ObpEy-NOZhlsWeVscEMmn9qgc187TRi9jR_8,23322
sqlalchemy/ext/declarative/base.py,sha256=ZVSQ-6ifPKpnSyoD4OjZk_oJUkgMPdRGi-Obbn6C6MM,25290
sqlalchemy/ext/declarative/clsregistry.py,sha256=jaLLSr-66XvLnA1Z9kxjKatH_XHxWchqEXMKwvjKAXk,10817
sqlalchemy/orm/__init__.py,sha256=UzDockQEVMaWvr-FE4y1rptrMb5uX5k8v_UNQs82qFY,8033
sqlalchemy/orm/attributes.py,sha256=OmXkppJEZxRGc0acZZZkSbUhdfDl8ry3Skmvzl3OtLQ,56510
sqlalchemy/orm/base.py,sha256=nS21na3Yx76UJzhWjzPLud1Ny0Xbmqx2DZQpVpHxHQM,14668
sqlalchemy/orm/collections.py,sha256=TFutWIn_c07DI48FDOKMsFMnAoQB3BG2FnEMGzEF3iI,53549
sqlalchemy/orm/dependency.py,sha256=phB8nS1788FSd4dWa2j9d4uj6QFlRL7nzcXvh3Bb7Zo,46192
sqlalchemy/orm/deprecated_interfaces.py,sha256=A63t6ivbZB3Wq8vWgL8I05uTRR6whcWnIPkquuTIPXU,18254
sqlalchemy/orm/descriptor_props.py,sha256=uk5r77w1VUWVgn0bkgOItkAlMh9FRgeT6OCgOHz3_bM,25141
sqlalchemy/orm/dynamic.py,sha256=I_YP7X-H9HLjeFHmYgsOas6JPdqg0Aqe0kaltt4HVzA,13283
sqlalchemy/orm/evaluator.py,sha256=o9E_mF3gPRa9HF_pNu-5twDe7865eFgO1FSCfoUB71s,4813
sqlalchemy/orm/events.py,sha256=yRaoXlBL78b3l11itTrAy42UhLu42-7cgXKCFUGNXSg,69410
sqlalchemy/orm/exc.py,sha256=P5lxi5RMFokiHL136VBK0AP3UmAlJcSDHtzgo-M6Kgs,5439
sqlalchemy/orm/identity.py,sha256=zsb8xOZaPYKvs4sGhyxW21mILQDrtdSuzD4sTyeKdJs,9021
sqlalchemy/orm/instrumentation.py,sha256=xtq9soM3mpMws7xqNJIFYXqKw65p2nnxCTfmMpuvpeI,17510
sqlalchemy/orm/interfaces.py,sha256=AqitvZ_BBkB6L503uhdH55nxHplleJ2kQMwM7xKq9Sc,21552
sqlalchemy/orm/loading.py,sha256=ZlxQszfG776WPVd5EHzPMdYat5IgmFltQ7QErMU3dtI,22885
sqlalchemy/orm/mapper.py,sha256=LKnaVLyDk4AjTdANzDq03R2XyI-SlkSsT50dUaOlsqI,115131
sqlalchemy/orm/path_registry.py,sha256=8Pah0P8yPVUyRjoET7DvIMGtM5PC8HZJC4GtxAyqVAs,8370
sqlalchemy/orm/persistence.py,sha256=WzUUNm1UGm5mGxbv94hLTQowEDNoXfU1VoyGnoKeN_g,51028
sqlalchemy/orm/properties.py,sha256=HR3eoY3Ze3FUPPNCXM_FruWz4pEMWrGlqtCGiK2G1qE,10426
sqlalchemy/orm/query.py,sha256=frir4h863dRbadKCdys5XeBClZ-SDcvupVgKLaN6Dlo,148267
sqlalchemy/orm/relationships.py,sha256=79LRGGz8MxsKsAlv0vuZ6MYZXzDXXtfiOCZg-IQ9hiU,116992
sqlalchemy/orm/scoping.py,sha256=Ao-K4iqg4pBp7Si5JOAlro5zUL_r500TC3lVLcFMLDs,6421
sqlalchemy/orm/session.py,sha256=yctpvCsLUcFv9Sy8keT1SElZ2VH5DNScYtO7Z77ptYI,111314
sqlalchemy/orm/state.py,sha256=4LwwftOtPQldH12SKZV2UFgzqPOCj40QfQ08knZs0_E,22984
sqlalchemy/orm/strategies.py,sha256=wvl6-ZwL28YJsJPY6xuHAYYnjk8k9fAbYqupvfvQ_Xo,58947
sqlalchemy/orm/strategy_options.py,sha256=LqJWCzML6YCI_toThom_bvfZQAOj6WIb3MrO9K7K6bo,34974
sqlalchemy/orm/sync.py,sha256=B-d-H1Gzw1TkflpvgJeQghwTzqObzhZCQdvEdSPyDeE,5451
sqlalchemy/orm/unitofwork.py,sha256=EQvZ7RZ-u5wJT51BWTeMJJi-tt22YRnmqywGUCn0Qrc,23343
sqlalchemy/orm/util.py,sha256=hMlZbgA8Qji9--EAahwzHsyKeR_GCEKwFZWjR4kgPsk,38077
sqlalchemy/sql/__init__.py,sha256=IFCJYIilmmAQRnSDhv9Y6LQUSpx6pUU5zp9VT7sOx0c,1737
sqlalchemy/sql/annotation.py,sha256=8ncgAVUo5QCoinApKjREi8esWNMFklcBqie8Q42KsaQ,6136
sqlalchemy/sql/base.py,sha256=TuXOp7z0Q30qKAjhgcsts6WGvRbvg6F7OBojMQAxjX0,20990
sqlalchemy/sql/compiler.py,sha256=4szeiIUoO6kgj37d8skkDVdPJw5ZxYW6KmyTDDmnK3U,100569
sqlalchemy/sql/crud.py,sha256=b-o2vT2CV2hIxdky9NpzvgEMHjbKxvF4tMgGdU4mLvs,19837
sqlalchemy/sql/ddl.py,sha256=nkjd_B4lKwC2GeyPjE0ZtRB9RKXccQL1g1XoZ4p69sM,37540
sqlalchemy/sql/default_comparator.py,sha256=NqvS_PgULqSaeJR25IjB2mUMy0R2O8avX0i9ge-gDi4,10464
sqlalchemy/sql/dml.py,sha256=7846H52IMJfMYi5Jd-Cv6Hy9hZM4dkonXbjfBjl5ED4,33330
sqlalchemy/sql/elements.py,sha256=okDQjYYhPucEX5OmP6XFxWUauFLrpV3ucwIisTSVVGE,133812
sqlalchemy/sql/expression.py,sha256=vFZ9MmBlC9Fg8IYzLMAwXgcsnXZhkZbUstY6dO8BFGY,5833
sqlalchemy/sql/functions.py,sha256=CV-L1qZDfNx378--oh_g6I7BQomMfDrjOmwNT6JxkAA,18669
sqlalchemy/sql/naming.py,sha256=foE2lAzngLCFXCeHrpv0S4zT23GCnZLCiata2MPo0kE,4662
sqlalchemy/sql/operators.py,sha256=UeZgb7eRhWd4H7OfJZkx0ZWOjvo5chIUXQsBAIeeTDY,23013
sqlalchemy/sql/schema.py,sha256=df0R2rXhjwCkKGHDshHy_mdrYJvWQ27OjAqhvk-xy9E,147681
sqlalchemy/sql/selectable.py,sha256=BT5RdjMrV026gLX1ThF_q1NFtEr2Op78yS-OUi3SYsc,119573
sqlalchemy/sql/sqltypes.py,sha256=JGxizqIjO1WFuZpppWj1Yi5cvCyBczb1JqUQeuhQn8s,54879
sqlalchemy/sql/type_api.py,sha256=LDXlmstH0rIVosJy2KioqRq2eO25EHV9NJeLlKuZD88,42846
sqlalchemy/sql/util.py,sha256=GhTktynNUK9LROR9YYSO0idy6mu6riDUkm-gt8bkfYI,20629
sqlalchemy/sql/visitors.py,sha256=4ipGvAkqFaSAWgyNuKjx5x_ms8GIy9aq-wC5pj4-Z3g,10271
sqlalchemy/testing/__init__.py,sha256=MwKimX0atzs_SmG2j74GXLiyI8O56e3DLq96tcoL0TM,1095
sqlalchemy/testing/assertions.py,sha256=r1I2nHC599VZcY-5g0JYRQl8bl9kjkf6WFOooOmJ2eE,16112
sqlalchemy/testing/assertsql.py,sha256=-fP9Iuhdu52BJoT1lEj_KED8jy5ay_XiJu7i4Ry9eWA,12335
sqlalchemy/testing/config.py,sha256=nqvVm55Vk0BVNjk1Wj3aYR65j_EEEepfB-W9QSFLU-k,2469
sqlalchemy/testing/distutils_run.py,sha256=tkURrZRwgFiSwseKm1iJRkSjKf2Rtsb3pOXRWtACTHI,247
sqlalchemy/testing/engines.py,sha256=u6GlDMXt0FKqVTQe_QJ5JXAnkA6W-xdw6Fe_5gMAQhg,9359
sqlalchemy/testing/entities.py,sha256=IXqTgAihV-1TZyxL0MWdZzu4rFtxdbWKWFetIJWNGM4,2992
sqlalchemy/testing/exclusions.py,sha256=WuH_tVK5fZJWe8Hu2LzNB4HNQMa_iAUaGC-_6mHUdIM,12570
sqlalchemy/testing/fixtures.py,sha256=q4nK-81z2EWs17TjeJtPmnaJUCtDdoUiIU7jgLq3l_w,10721
sqlalchemy/testing/mock.py,sha256=vj5q-GzJrLW6mMVDLqsppxBu_p7K49VvjfiVt5tn0o8,630
sqlalchemy/testing/pickleable.py,sha256=8I8M4H1XN29pZPMxZdYkmpKWfwzPsUn6WK5FX4UP9L4,2641
sqlalchemy/testing/profiling.py,sha256=Q_wOTS5JtcGBcs2eCYIvoRoDS_FW_HcfEW3hXWB87Zg,8392
sqlalchemy/testing/provision.py,sha256=DW1OD8lXK4mv0E0ibTPJoq_TwjBSNFPzrk0OktbZXDw,9388
sqlalchemy/testing/replay_fixture.py,sha256=iAxg7XsFkKSCcJnrNPQNJfjMxOgeBAa-ShOkywWPJ4w,5429
sqlalchemy/testing/requirements.py,sha256=aIdvbfugMzrlVdldEbpcwretX-zjiukPhPUSZgulrzU,19949
sqlalchemy/testing/runner.py,sha256=hpNH6MNTif4TnBRySxpm92KgFwDK0mOa8eF7wZXumTI,1607
sqlalchemy/testing/schema.py,sha256=agOzrIMvmuUCeVZY5mYjJ1eJmOP69-wa0gZALtNtJBk,3446
sqlalchemy/testing/util.py,sha256=IJ688AWzichtXVwWgYf_A4BUbcXPGsK6BQP5fvY3h-U,7544
sqlalchemy/testing/warnings.py,sha256=-KskRAh1RkJ_69UIY_WR7i15u21U3gDLQ6nKlnJT7_w,987
sqlalchemy/testing/plugin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
sqlalchemy/testing/plugin/bootstrap.py,sha256=Iw8R-d1gqoz_NKFtPyGfdX56QPcQHny_9Lvwov65aVY,1634
sqlalchemy/testing/plugin/noseplugin.py,sha256=In79x6zs9DOngfoYpaHojihWlSd4PeS7Nwzh3M_KNM4,2847
sqlalchemy/testing/plugin/plugin_base.py,sha256=bZvCglPhWVY6dyrkWFEjuJ3ccjt3VdRvbjE6oeNil8o,17335
sqlalchemy/testing/plugin/pytestplugin.py,sha256=o29RaERGgyakyA3ovnGzCq7rPBSjp6Gjf31Yy9jLw-4,6146
sqlalchemy/testing/suite/__init__.py,sha256=wqCTrb28i5FwhQZOyXVlnz3mA94iQOUBio7lszkFq-g,471
sqlalchemy/testing/suite/test_ddl.py,sha256=Baw0ou9nKdADmrRuXgWzF1FZx0rvkkw3JHc6yw5BN0M,1838
sqlalchemy/testing/suite/test_dialect.py,sha256=ORQPXUt53XtO-5ENlWgs8BpsSdPBDjyMRl4W2UjXLI4,1165
sqlalchemy/testing/suite/test_insert.py,sha256=nP0mgVpsVs72MHMADmihB1oXLbFBpsYsLGO3BlQ7RLU,8132
sqlalchemy/testing/suite/test_reflection.py,sha256=HtJRsJ_vuNMrOhnPTvuIvRg66OakSaSpeCU36zhaSPg,24616
sqlalchemy/testing/suite/test_results.py,sha256=oAcO1tD0I7c9ErMeSvSZBZfz1IBDMJHJTf64Y1pBodk,6685
sqlalchemy/testing/suite/test_select.py,sha256=u0wAz1g-GrAFdZpG4zwSrVckVtjULvjlbd0Z1U1jHAA,5729
sqlalchemy/testing/suite/test_sequence.py,sha256=fmBR4Pc5tOLSkXFxfcqwGx1z3xaxeJeUyqDnTakKTBU,3831
sqlalchemy/testing/suite/test_types.py,sha256=UKa-ZPdpz16mVKvT-9ISRAfqdrqiKaE7IA-_phQQuxo,17088
sqlalchemy/testing/suite/test_update_delete.py,sha256=r5p467r-EUsjEcWGfUE0VPIfN4LLXZpLRnnyBLyyjl4,1582
sqlalchemy/util/__init__.py,sha256=G06a5vBxg27RtWzY6dPZHt1FO8qtOiy_2C9PHTTMblI,2520
sqlalchemy/util/_collections.py,sha256=s41rf2YrYEfl6MTIp3x3WMzLvGP532PR9FD8-2VwivA,27889
sqlalchemy/util/compat.py,sha256=gInErUyI0XdS590SIFSbjdmIdwn-hxFVyWYU12p_QqM,6873
sqlalchemy/util/deprecations.py,sha256=D_LTsfb9jHokJtPEWNDRMJOc372xRGNjputAiTIysRU,4403
sqlalchemy/util/langhelpers.py,sha256=Nhe3Y9ieK6JaFYejjYosVOjOSSIBT2V385Hu6HGcyZk,41607
sqlalchemy/util/queue.py,sha256=rs3W0LDhKt7M_dlQEjYpI9KS-bzQmmwN38LE_-RRVvU,6548
sqlalchemy/util/topological.py,sha256=xKsYjjAat4p8cdqRHKwibLzr6WONbPTC0X8Mqg7jYno,2794
SQLAlchemy-1.0.15.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
sqlalchemy/testing/__pycache__/entities.cpython-34.pyc,,
sqlalchemy/dialects/sybase/__pycache__/base.cpython-34.pyc,,
sqlalchemy/engine/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/dialects/mysql/__pycache__/mysqldb.cpython-34.pyc,,
sqlalchemy/connectors/__pycache__/mxodbc.cpython-34.pyc,,
sqlalchemy/sql/__pycache__/compiler.cpython-34.pyc,,
sqlalchemy/__pycache__/log.cpython-34.pyc,,
sqlalchemy/sql/__pycache__/base.cpython-34.pyc,,
sqlalchemy/dialects/mssql/__pycache__/pyodbc.cpython-34.pyc,,
sqlalchemy/testing/__pycache__/distutils_run.cpython-34.pyc,,
sqlalchemy/dialects/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/dialects/oracle/__pycache__/base.cpython-34.pyc,,
sqlalchemy/testing/suite/__pycache__/test_types.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/events.cpython-34.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/ranges.cpython-34.pyc,,
sqlalchemy/engine/__pycache__/interfaces.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/state.cpython-34.pyc,,
sqlalchemy/ext/declarative/__pycache__/api.cpython-34.pyc,,
sqlalchemy/testing/__pycache__/mock.cpython-34.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/base.cpython-34.pyc,,
sqlalchemy/sql/__pycache__/crud.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/evaluator.cpython-34.pyc,,
sqlalchemy/__pycache__/interfaces.cpython-34.pyc,,
sqlalchemy/engine/__pycache__/util.cpython-34.pyc,,
sqlalchemy/sql/__pycache__/elements.cpython-34.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/base.cpython-34.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/testing/__pycache__/provision.cpython-34.pyc,,
sqlalchemy/dialects/sybase/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/deprecated_interfaces.cpython-34.pyc,,
sqlalchemy/testing/__pycache__/assertions.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/scoping.cpython-34.pyc,,
sqlalchemy/dialects/mysql/__pycache__/cymysql.cpython-34.pyc,,
sqlalchemy/ext/__pycache__/mutable.cpython-34.pyc,,
sqlalchemy/ext/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/zxjdbc.cpython-34.pyc,,
sqlalchemy/testing/__pycache__/requirements.cpython-34.pyc,,
sqlalchemy/dialects/firebird/__pycache__/base.cpython-34.pyc,,
sqlalchemy/connectors/__pycache__/zxJDBC.cpython-34.pyc,,
sqlalchemy/testing/__pycache__/fixtures.cpython-34.pyc,,
sqlalchemy/ext/declarative/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/event/__pycache__/legacy.cpython-34.pyc,,
sqlalchemy/connectors/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/dialects/oracle/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/path_registry.cpython-34.pyc,,
sqlalchemy/testing/__pycache__/assertsql.cpython-34.pyc,,
sqlalchemy/dialects/mssql/__pycache__/zxjdbc.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/identity.cpython-34.pyc,,
sqlalchemy/__pycache__/processors.cpython-34.pyc,,
sqlalchemy/util/__pycache__/langhelpers.cpython-34.pyc,,
sqlalchemy/testing/suite/__pycache__/test_insert.cpython-34.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/pg8000.cpython-34.pyc,,
sqlalchemy/util/__pycache__/_collections.cpython-34.pyc,,
sqlalchemy/event/__pycache__/api.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/exc.cpython-34.pyc,,
sqlalchemy/testing/suite/__pycache__/test_update_delete.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/collections.cpython-34.pyc,,
sqlalchemy/testing/__pycache__/exclusions.cpython-34.pyc,,
sqlalchemy/ext/__pycache__/horizontal_shard.cpython-34.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/json.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/util.cpython-34.pyc,,
sqlalchemy/dialects/sybase/__pycache__/pyodbc.cpython-34.pyc,,
sqlalchemy/sql/__pycache__/util.cpython-34.pyc,,
sqlalchemy/ext/__pycache__/hybrid.cpython-34.pyc,,
sqlalchemy/__pycache__/pool.cpython-34.pyc,,
sqlalchemy/dialects/mssql/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/engine/__pycache__/strategies.cpython-34.pyc,,
sqlalchemy/sql/__pycache__/schema.cpython-34.pyc,,
sqlalchemy/dialects/mssql/__pycache__/mxodbc.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/unitofwork.cpython-34.pyc,,
sqlalchemy/dialects/mssql/__pycache__/information_schema.cpython-34.pyc,,
sqlalchemy/sql/__pycache__/operators.cpython-34.pyc,,
sqlalchemy/sql/__pycache__/ddl.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/strategies.cpython-34.pyc,,
sqlalchemy/engine/__pycache__/url.cpython-34.pyc,,
sqlalchemy/testing/__pycache__/config.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/properties.cpython-34.pyc,,
sqlalchemy/ext/declarative/__pycache__/clsregistry.cpython-34.pyc,,
sqlalchemy/testing/plugin/__pycache__/noseplugin.cpython-34.pyc,,
sqlalchemy/dialects/mysql/__pycache__/base.cpython-34.pyc,,
sqlalchemy/testing/suite/__pycache__/test_sequence.cpython-34.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/pysqlcipher.cpython-34.pyc,,
sqlalchemy/testing/plugin/__pycache__/plugin_base.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/mapper.cpython-34.pyc,,
sqlalchemy/dialects/mysql/__pycache__/pymysql.cpython-34.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/dialects/__pycache__/postgres.cpython-34.pyc,,
sqlalchemy/dialects/firebird/__pycache__/kinterbasdb.cpython-34.pyc,,
sqlalchemy/engine/__pycache__/reflection.cpython-34.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/pysqlite.cpython-34.pyc,,
sqlalchemy/sql/__pycache__/naming.cpython-34.pyc,,
sqlalchemy/testing/plugin/__pycache__/pytestplugin.cpython-34.pyc,,
sqlalchemy/engine/__pycache__/default.cpython-34.pyc,,
sqlalchemy/ext/__pycache__/orderinglist.cpython-34.pyc,,
sqlalchemy/dialects/oracle/__pycache__/cx_oracle.cpython-34.pyc,,
sqlalchemy/dialects/mssql/__pycache__/adodbapi.cpython-34.pyc,,
sqlalchemy/__pycache__/types.cpython-34.pyc,,
sqlalchemy/sql/__pycache__/sqltypes.cpython-34.pyc,,
sqlalchemy/sql/__pycache__/annotation.cpython-34.pyc,,
sqlalchemy/sql/__pycache__/selectable.cpython-34.pyc,,
sqlalchemy/ext/__pycache__/baked.cpython-34.pyc,,
sqlalchemy/testing/plugin/__pycache__/bootstrap.cpython-34.pyc,,
sqlalchemy/testing/suite/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/sql/__pycache__/functions.cpython-34.pyc,,
sqlalchemy/ext/declarative/__pycache__/base.cpython-34.pyc,,
sqlalchemy/dialects/mysql/__pycache__/zxjdbc.cpython-34.pyc,,
sqlalchemy/ext/__pycache__/serializer.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/dependency.cpython-34.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/pypostgresql.cpython-34.pyc,,
sqlalchemy/event/__pycache__/base.cpython-34.pyc,,
sqlalchemy/sql/__pycache__/type_api.cpython-34.pyc,,
sqlalchemy/dialects/sybase/__pycache__/mxodbc.cpython-34.pyc,,
sqlalchemy/testing/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/base.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/relationships.cpython-34.pyc,,
sqlalchemy/dialects/mysql/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/__pycache__/events.cpython-34.pyc,,
sqlalchemy/testing/plugin/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/ext/__pycache__/associationproxy.cpython-34.pyc,,
sqlalchemy/dialects/oracle/__pycache__/zxjdbc.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/interfaces.cpython-34.pyc,,
sqlalchemy/__pycache__/inspection.cpython-34.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/psycopg2.cpython-34.pyc,,
sqlalchemy/event/__pycache__/attr.cpython-34.pyc,,
sqlalchemy/testing/suite/__pycache__/test_reflection.cpython-34.pyc,,
sqlalchemy/engine/__pycache__/result.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/sync.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/loading.cpython-34.pyc,,
sqlalchemy/util/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/dialects/sybase/__pycache__/pysybase.cpython-34.pyc,,
sqlalchemy/event/__pycache__/registry.cpython-34.pyc,,
sqlalchemy/testing/__pycache__/schema.cpython-34.pyc,,
sqlalchemy/engine/__pycache__/threadlocal.cpython-34.pyc,,
sqlalchemy/sql/__pycache__/default_comparator.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/strategy_options.cpython-34.pyc,,
sqlalchemy/dialects/firebird/__pycache__/fdb.cpython-34.pyc,,
sqlalchemy/testing/__pycache__/runner.cpython-34.pyc,,
sqlalchemy/testing/suite/__pycache__/test_select.cpython-34.pyc,,
sqlalchemy/testing/__pycache__/engines.cpython-34.pyc,,
sqlalchemy/engine/__pycache__/base.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/session.cpython-34.pyc,,
sqlalchemy/connectors/__pycache__/pyodbc.cpython-34.pyc,,
sqlalchemy/util/__pycache__/deprecations.cpython-34.pyc,,
sqlalchemy/testing/suite/__pycache__/test_results.cpython-34.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/constraints.cpython-34.pyc,,
sqlalchemy/sql/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/ext/__pycache__/instrumentation.cpython-34.pyc,,
sqlalchemy/__pycache__/schema.cpython-34.pyc,,
sqlalchemy/sql/__pycache__/dml.cpython-34.pyc,,
sqlalchemy/util/__pycache__/topological.cpython-34.pyc,,
sqlalchemy/dialects/mysql/__pycache__/oursql.cpython-34.pyc,,
sqlalchemy/ext/__pycache__/compiler.cpython-34.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/hstore.cpython-34.pyc,,
sqlalchemy/testing/suite/__pycache__/test_ddl.cpython-34.pyc,,
sqlalchemy/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/dialects/mssql/__pycache__/base.cpython-34.pyc,,
sqlalchemy/ext/__pycache__/automap.cpython-34.pyc,,
sqlalchemy/util/__pycache__/compat.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/query.cpython-34.pyc,,
sqlalchemy/testing/suite/__pycache__/test_dialect.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/dynamic.cpython-34.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/psycopg2cffi.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/descriptor_props.cpython-34.pyc,,
sqlalchemy/testing/__pycache__/replay_fixture.cpython-34.pyc,,
sqlalchemy/testing/__pycache__/warnings.cpython-34.pyc,,
sqlalchemy/dialects/firebird/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/__pycache__/exc.cpython-34.pyc,,
sqlalchemy/testing/__pycache__/util.cpython-34.pyc,,
sqlalchemy/util/__pycache__/queue.cpython-34.pyc,,
sqlalchemy/testing/__pycache__/pickleable.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/attributes.cpython-34.pyc,,
sqlalchemy/dialects/mysql/__pycache__/pyodbc.cpython-34.pyc,,
sqlalchemy/sql/__pycache__/expression.cpython-34.pyc,,
sqlalchemy/databases/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/dialects/mysql/__pycache__/mysqlconnector.cpython-34.pyc,,
sqlalchemy/dialects/mssql/__pycache__/pymssql.cpython-34.pyc,,
sqlalchemy/event/__pycache__/__init__.cpython-34.pyc,,
sqlalchemy/testing/__pycache__/profiling.cpython-34.pyc,,
sqlalchemy/sql/__pycache__/visitors.cpython-34.pyc,,
sqlalchemy/dialects/mysql/__pycache__/gaerdbms.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/instrumentation.cpython-34.pyc,,
sqlalchemy/orm/__pycache__/persistence.cpython-34.pyc,,

View File

@@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.29.0)
Root-Is-Purelib: false
Tag: cp34-cp34m-linux_armv7l

View File

@@ -0,0 +1 @@
{"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: Jython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Database :: Front-Ends", "Operating System :: OS Independent"], "extensions": {"python.details": {"contacts": [{"email": "mike_mp@zzzcomputing.com", "name": "Mike Bayer", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://www.sqlalchemy.org"}}}, "generator": "bdist_wheel (0.29.0)", "license": "MIT License", "metadata_version": "2.0", "name": "SQLAlchemy", "summary": "Database Abstraction Library", "test_requires": [{"requires": ["mock", "pytest (>=2.5.2)", "pytest-xdist"]}], "version": "1.0.15"}

View File

@@ -0,0 +1 @@
sqlalchemy

View File

@@ -0,0 +1,54 @@
Werkzeug
========
Werkzeug started as simple collection of various utilities for WSGI
applications and has become one of the most advanced WSGI utility
modules. It includes a powerful debugger, full featured request and
response objects, HTTP utilities to handle entity tags, cache control
headers, HTTP dates, cookie handling, file uploads, a powerful URL
routing system and a bunch of community contributed addon modules.
Werkzeug is unicode aware and doesn't enforce a specific template
engine, database adapter or anything else. It doesn't even enforce
a specific way of handling requests and leaves all that up to the
developer. It's most useful for end user applications which should work
on as many server environments as possible (such as blogs, wikis,
bulletin boards, etc.).
Details and example applications are available on the
`Werkzeug website <http://werkzeug.pocoo.org/>`_.
Features
--------
- unicode awareness
- request and response objects
- various utility functions for dealing with HTTP headers such as
`Accept` and `Cache-Control` headers.
- thread local objects with proper cleanup at request end
- an interactive debugger
- A simple WSGI server with support for threading and forking
with an automatic reloader.
- a flexible URL routing system with REST support.
- fully WSGI compatible
Development Version
-------------------
The Werkzeug development version can be installed by cloning the git
repository from `github`_::
git clone git@github.com:mitsuhiko/werkzeug.git
.. _github: http://github.com/mitsuhiko/werkzeug

View File

@@ -0,0 +1 @@
pip

View File

@@ -0,0 +1,79 @@
Metadata-Version: 2.0
Name: Werkzeug
Version: 0.11.11
Summary: The Swiss Army knife of Python web development
Home-page: http://werkzeug.pocoo.org/
Author: Armin Ronacher
Author-email: armin.ronacher@active-4.com
License: BSD
Platform: any
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Werkzeug
========
Werkzeug started as simple collection of various utilities for WSGI
applications and has become one of the most advanced WSGI utility
modules. It includes a powerful debugger, full featured request and
response objects, HTTP utilities to handle entity tags, cache control
headers, HTTP dates, cookie handling, file uploads, a powerful URL
routing system and a bunch of community contributed addon modules.
Werkzeug is unicode aware and doesn't enforce a specific template
engine, database adapter or anything else. It doesn't even enforce
a specific way of handling requests and leaves all that up to the
developer. It's most useful for end user applications which should work
on as many server environments as possible (such as blogs, wikis,
bulletin boards, etc.).
Details and example applications are available on the
`Werkzeug website <http://werkzeug.pocoo.org/>`_.
Features
--------
- unicode awareness
- request and response objects
- various utility functions for dealing with HTTP headers such as
`Accept` and `Cache-Control` headers.
- thread local objects with proper cleanup at request end
- an interactive debugger
- A simple WSGI server with support for threading and forking
with an automatic reloader.
- a flexible URL routing system with REST support.
- fully WSGI compatible
Development Version
-------------------
The Werkzeug development version can be installed by cloning the git
repository from `github`_::
git clone git@github.com:mitsuhiko/werkzeug.git
.. _github: http://github.com/mitsuhiko/werkzeug

94
deps/Werkzeug-0.11.11.dist-info/RECORD vendored Normal file
View File

@@ -0,0 +1,94 @@
werkzeug/posixemulation.py,sha256=xEF2Bxc-vUCPkiu4IbfWVd3LW7DROYAT-ExW6THqyzw,3519
werkzeug/security.py,sha256=tuVc22OqoHV5K-TrYJmynCJJa12aUt9BQ3wR_vEPQ34,8971
werkzeug/__init__.py,sha256=7sfvCWELBhe774-fzDoL1EU8GHsNG7iuxOkF0OnE5cc,6920
werkzeug/testapp.py,sha256=3HQRW1sHZKXuAjCvFMet4KXtQG3loYTFnvn6LWt-4zI,9396
werkzeug/http.py,sha256=i9tuTlN8sOy9car-esVsHET6AfNCnObbu1pRBjqExvs,35287
werkzeug/routing.py,sha256=TqiZD5HkwdLBnKBUjC5PlytzXmpczQC5dz54VfQzMOw,66350
werkzeug/utils.py,sha256=lkybtv_mq35zV1qhelvEcILTzrMUwZ9yon6E8XwapJE,22972
werkzeug/exceptions.py,sha256=c-3fKHItsPvC52X_NwBNLcmGXR30h0WP5ynPSwCqPiw,18733
werkzeug/_reloader.py,sha256=YQykMSQW7AlojJQ7qOlgNaXw5_CNjf9yzxplwzVdL7Q,8336
werkzeug/formparser.py,sha256=90D5Urp8Ghrzw32kAs090G0nXPYlU73NeAzPlQFMVrY,21296
werkzeug/_compat.py,sha256=8c4U9o6A_TR9nKCcTbpZNxpqCXcXDVIbFawwKM2s92c,6311
werkzeug/datastructures.py,sha256=-W9uZXQ-HSAy_lGDh5oO6cPUE8bNtXoNQ6BaFG8B2Vs,87575
werkzeug/wrappers.py,sha256=lKYevpKD1-quk9Cop7bsFxt1eWJxU3h33HCnOI_YzSU,77011
werkzeug/test.py,sha256=pQMDJjKdjZVWd_BJfnXExE3NH5Ykr-LG5YT4giptWyw,34127
werkzeug/urls.py,sha256=fSbI4Gb29_p02Zk21VAZQRN1QdOVY9CNTgpb2rbajNQ,36710
werkzeug/script.py,sha256=DwaVDcXdaOTffdNvlBdLitxWXjKaRVT32VbhDtljFPY,11365
werkzeug/useragents.py,sha256=uqpgPcJ5BfcCVh9nPIIl2r3duIrIuENmrbRqbAMmPDk,5418
werkzeug/local.py,sha256=4Q5gwHQJhfhZFqTR8iQDs2VHohpR1OEsP4YTwn7rt7w,14275
werkzeug/serving.py,sha256=uRUqXuA-Dw2MRA-d232cK_034-taldoj66fEFrtin7k,27736
werkzeug/filesystem.py,sha256=hHWeWo_gqLMzTRfYt8-7n2wWcWUNTnDyudQDLOBEICE,2175
werkzeug/wsgi.py,sha256=S8R3pBGPlBK67s-d6Wa93nhzG27WjfcHs_ZBGIAQCxM,39573
werkzeug/_internal.py,sha256=IEScSoFtQ8KqFH_2ubdfscNAdQ2RIysyVupI5BR9W2U,13709
werkzeug/contrib/fixers.py,sha256=MtN_YmENxoTsGvXGGERmtbQ62LaeFc5I2d1YifXNENA,10183
werkzeug/contrib/limiter.py,sha256=iS8-ahPZ-JLRnmfIBzxpm7O_s3lPsiDMVWv7llAIDCI,1334
werkzeug/contrib/__init__.py,sha256=f7PfttZhbrImqpr5Ezre8CXgwvcGUJK7zWNpO34WWrw,623
werkzeug/contrib/testtools.py,sha256=G9xN-qeihJlhExrIZMCahvQOIDxdL9NiX874jiiHFMs,2453
werkzeug/contrib/iterio.py,sha256=pTX36rYCKO_9IEoB5sIN5cFSYszI9zdx6YhquWovcPY,10814
werkzeug/contrib/cache.py,sha256=4W2WCT9Hw6HEU8yME9GuU4Xf8e50r2K84ASMxhLb6tY,27983
werkzeug/contrib/securecookie.py,sha256=X-Ao_0NRDveW6K1Fhe4U42hHWBW8esCpA3VcBDpzWIk,12206
werkzeug/contrib/lint.py,sha256=XDKYx0ELn9k18xRn4SiAsCgltCuN4yLjzxnCN8tG_eM,12490
werkzeug/contrib/profiler.py,sha256=ISwCWvwVyGpDLRBRpLjo_qUWma6GXYBrTAco4PEQSHY,5151
werkzeug/contrib/wrappers.py,sha256=zcd-1yC-kZQOLnn8Bs2SzKUNn7z2H9f0DpcVYS5Ty8s,10380
werkzeug/contrib/atom.py,sha256=rvijBrphjMzVObfuCR6ddu6aLwI_SiNiudu64OSTh4Q,15588
werkzeug/contrib/jsrouting.py,sha256=QTmgeDoKXvNK02KzXgx9lr3cAH6fAzpwF5bBdPNvJPs,8564
werkzeug/contrib/sessions.py,sha256=uAPcnyxaxEla-bUA13gKc3KK4mwSagdzbCZzyKl3PeE,12577
werkzeug/debug/console.py,sha256=B7uAu9Rk60siDnGlEt-A_q1ZR4zCtmxx5itg3X-BOxo,5599
werkzeug/debug/repr.py,sha256=NaoB89aHb0vuvdSWels-GWdeGDZp76uE4uSNZPX1jAM,9354
werkzeug/debug/__init__.py,sha256=qQT5YnOv9Eov9Jt5eLtP6MOqwpmo-tORJ6HcQmmnvro,17271
werkzeug/debug/tbtools.py,sha256=-BeFH40ISiF5DFn9RfHMQzCGzmFBovZnREyj-lLzptM,18410
werkzeug/debug/shared/less.png,sha256=-4-kNRaXJSONVLahrQKUxMwXGm9R4OnZ9SxDGpHlIR4,191
werkzeug/debug/shared/source.png,sha256=RoGcBTE4CyCB85GBuDGTFlAnUqxwTBiIfDqW15EpnUQ,818
werkzeug/debug/shared/debugger.js,sha256=PEMBoNuD6fUaNou8Km_ZvVmFcIA3z3k3jSEMWLW-cA0,6187
werkzeug/debug/shared/style.css,sha256=7x1s8olZO1XHalqD4M9MWn9vRqQkA635S9_6zRoe220,6231
werkzeug/debug/shared/console.png,sha256=bxax6RXXlvOij_KeqvSNX0ojJf83YbnZ7my-3Gx9w2A,507
werkzeug/debug/shared/FONT_LICENSE,sha256=LwAVEI1oYnvXiNMT9SnCH_TaLCxCpeHziDrMg0gPkAI,4673
werkzeug/debug/shared/jquery.js,sha256=7LkWEzqTdpEfELxcZZlS6wAx5Ff13zZ83lYO2_ujj7g,95957
werkzeug/debug/shared/ubuntu.ttf,sha256=1eaHFyepmy4FyDvjLVzpITrGEBu_CZYY94jE0nED1c0,70220
werkzeug/debug/shared/more.png,sha256=GngN7CioHQoV58rH6ojnkYi8c_qED2Aka5FO5UXrReY,200
Werkzeug-0.11.11.dist-info/DESCRIPTION.rst,sha256=5sTwZ_Sj5aeEN8mlcOdNJ_ng40HiGazGmILLyTMX8o0,1595
Werkzeug-0.11.11.dist-info/metadata.json,sha256=aFs0-WetLKLo-qWj4IIgXNtkOaDtX5H6YtZKJuHJVBc,1096
Werkzeug-0.11.11.dist-info/RECORD,,
Werkzeug-0.11.11.dist-info/top_level.txt,sha256=QRyj2VjwJoQkrwjwFIOlB8Xg3r9un0NtqVHQF-15xaw,9
Werkzeug-0.11.11.dist-info/WHEEL,sha256=AvR0WeTpDaxT645bl5FQxUK6NPsTls2ttpcGJg3j1Xg,110
Werkzeug-0.11.11.dist-info/METADATA,sha256=yIzL70I-ZF_FswTj-dMsH_HvVIlmG8iqrq_lMsqp8WU,2600
Werkzeug-0.11.11.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
werkzeug/__pycache__/wsgi.cpython-34.pyc,,
werkzeug/__pycache__/wrappers.cpython-34.pyc,,
werkzeug/debug/__pycache__/console.cpython-34.pyc,,
werkzeug/contrib/__pycache__/jsrouting.cpython-34.pyc,,
werkzeug/debug/__pycache__/__init__.cpython-34.pyc,,
werkzeug/contrib/__pycache__/fixers.cpython-34.pyc,,
werkzeug/__pycache__/http.cpython-34.pyc,,
werkzeug/contrib/__pycache__/limiter.cpython-34.pyc,,
werkzeug/contrib/__pycache__/atom.cpython-34.pyc,,
werkzeug/__pycache__/posixemulation.cpython-34.pyc,,
werkzeug/__pycache__/__init__.cpython-34.pyc,,
werkzeug/__pycache__/formparser.cpython-34.pyc,,
werkzeug/__pycache__/useragents.cpython-34.pyc,,
werkzeug/contrib/__pycache__/iterio.cpython-34.pyc,,
werkzeug/__pycache__/serving.cpython-34.pyc,,
werkzeug/__pycache__/security.cpython-34.pyc,,
werkzeug/__pycache__/local.cpython-34.pyc,,
werkzeug/contrib/__pycache__/sessions.cpython-34.pyc,,
werkzeug/__pycache__/utils.cpython-34.pyc,,
werkzeug/contrib/__pycache__/lint.cpython-34.pyc,,
werkzeug/__pycache__/_reloader.cpython-34.pyc,,
werkzeug/__pycache__/datastructures.cpython-34.pyc,,
werkzeug/__pycache__/test.cpython-34.pyc,,
werkzeug/debug/__pycache__/repr.cpython-34.pyc,,
werkzeug/contrib/__pycache__/__init__.cpython-34.pyc,,
werkzeug/__pycache__/exceptions.cpython-34.pyc,,
werkzeug/__pycache__/_compat.cpython-34.pyc,,
werkzeug/contrib/__pycache__/testtools.cpython-34.pyc,,
werkzeug/contrib/__pycache__/profiler.cpython-34.pyc,,
werkzeug/contrib/__pycache__/cache.cpython-34.pyc,,
werkzeug/__pycache__/routing.cpython-34.pyc,,
werkzeug/__pycache__/_internal.cpython-34.pyc,,
werkzeug/__pycache__/filesystem.cpython-34.pyc,,
werkzeug/__pycache__/testapp.cpython-34.pyc,,
werkzeug/__pycache__/urls.cpython-34.pyc,,
werkzeug/contrib/__pycache__/securecookie.cpython-34.pyc,,
werkzeug/contrib/__pycache__/wrappers.cpython-34.pyc,,
werkzeug/debug/__pycache__/tbtools.cpython-34.pyc,,
werkzeug/__pycache__/script.cpython-34.pyc,,

6
deps/Werkzeug-0.11.11.dist-info/WHEEL vendored Normal file
View File

@@ -0,0 +1,6 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.24.0)
Root-Is-Purelib: true
Tag: py2-none-any
Tag: py3-none-any

View File

@@ -0,0 +1 @@
{"license": "BSD", "name": "Werkzeug", "metadata_version": "2.0", "generator": "bdist_wheel (0.24.0)", "summary": "The Swiss Army knife of Python web development", "platform": "any", "version": "0.11.11", "extensions": {"python.details": {"project_urls": {"Home": "http://werkzeug.pocoo.org/"}, "document_names": {"description": "DESCRIPTION.rst"}, "contacts": [{"role": "author", "email": "armin.ronacher@active-4.com", "name": "Armin Ronacher"}]}}, "classifiers": ["Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules"]}

View File

@@ -0,0 +1 @@
werkzeug

BIN
deps/__pycache__/astral.cpython-34.pyc vendored Normal file

Binary file not shown.

BIN
deps/__pycache__/phue.cpython-34.pyc vendored Normal file

Binary file not shown.

BIN
deps/__pycache__/six.cpython-34.pyc vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
deps/__pycache__/static.cpython-34.pyc vendored Normal file

Binary file not shown.

BIN
deps/__pycache__/zeroconf.cpython-34.pyc vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,22 @@
This is 'astral' a Python module which calculates
* Times for various positions of the sun: dawn, sunrise, solar noon,
sunset, dusk, solar elevation, solar azimuth and rahukaalam.
* The phase of the moon.
For documentation see the http://pythonhosted.org/astral/
GoogleGeocoder
--------------
`GoogleGeocoder` uses the mapping services provided by Google
Access to the `GoogleGeocoder` requires you to agree to be bound by
Google Maps/Google Earth APIs Terms of Service found at
https://developers.google.com/maps/terms which includes but is not limited to
having a Google Account.
More information on Google's maps service can be found at
https://developers.google.com/maps/documentation/

1
deps/astral-1.2.dist-info/INSTALLER vendored Normal file
View File

@@ -0,0 +1 @@
pip

37
deps/astral-1.2.dist-info/METADATA vendored Normal file
View File

@@ -0,0 +1,37 @@
Metadata-Version: 2.0
Name: astral
Version: 1.2
Summary: Calculations for the position of the sun and moon.
Home-page: https://launchpad.net/astral
Author: Simon Kennedy
Author-email: sffjunkie+code@gmail.com
License: Apache-2.0
Platform: UNKNOWN
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Requires-Dist: pytz
This is 'astral' a Python module which calculates
* Times for various positions of the sun: dawn, sunrise, solar noon,
sunset, dusk, solar elevation, solar azimuth and rahukaalam.
* The phase of the moon.
For documentation see the http://pythonhosted.org/astral/
GoogleGeocoder
--------------
`GoogleGeocoder` uses the mapping services provided by Google
Access to the `GoogleGeocoder` requires you to agree to be bound by
Google Maps/Google Earth APIs Terms of Service found at
https://developers.google.com/maps/terms which includes but is not limited to
having a Google Account.
More information on Google's maps service can be found at
https://developers.google.com/maps/documentation/

9
deps/astral-1.2.dist-info/RECORD vendored Normal file
View File

@@ -0,0 +1,9 @@
astral.py,sha256=2H2NtfjQifCApNMEshNuVREdZkmPT4ViYfRLdJR3MH8,90551
astral-1.2.dist-info/DESCRIPTION.rst,sha256=LnKJ3t_LHOBmmFcaTJecRsXZZgLgoh1n36C-4OrOkFg,712
astral-1.2.dist-info/METADATA,sha256=k7XKsuC2xSZ6m469EpMLcbHdXPkQ_B9RX00uowJzUqk,1165
astral-1.2.dist-info/RECORD,,
astral-1.2.dist-info/WHEEL,sha256=9Z5Xm-eel1bTS7e6ogYiKz0zmPEqDwIypurdHN1hR40,116
astral-1.2.dist-info/metadata.json,sha256=NRjJlS4IUbKVxDj42EqCtqYmOBjBlMYeFs7ehl7Lwyg,698
astral-1.2.dist-info/top_level.txt,sha256=XMiS6N151F3-8h-ah-eVWJbJarm3kfI0XlpFa8pET2E,7
astral-1.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
__pycache__/astral.cpython-34.pyc,,

6
deps/astral-1.2.dist-info/WHEEL vendored Normal file
View File

@@ -0,0 +1,6 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.29.0)
Root-Is-Purelib: true
Tag: py2-none-any
Tag: py3-none-any

View File

@@ -0,0 +1 @@
{"classifiers": ["Intended Audience :: Developers", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3"], "extensions": {"python.details": {"contacts": [{"email": "sffjunkie+code@gmail.com", "name": "Simon Kennedy", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://launchpad.net/astral"}}}, "extras": [], "generator": "bdist_wheel (0.29.0)", "license": "Apache-2.0", "metadata_version": "2.0", "name": "astral", "run_requires": [{"requires": ["pytz"]}], "summary": "Calculations for the position of the sun and moon.", "test_requires": [{"requires": ["tox"]}], "version": "1.2"}

View File

@@ -0,0 +1 @@
astral

2375
deps/astral.py vendored Normal file

File diff suppressed because it is too large Load Diff

375
deps/cherrypy/__init__.py vendored Normal file
View File

@@ -0,0 +1,375 @@
"""CherryPy is a pythonic, object-oriented HTTP framework.
CherryPy consists of not one, but four separate API layers.
The APPLICATION LAYER is the simplest. CherryPy applications are written as
a tree of classes and methods, where each branch in the tree corresponds to
a branch in the URL path. Each method is a 'page handler', which receives
GET and POST params as keyword arguments, and returns or yields the (HTML)
body of the response. The special method name 'index' is used for paths
that end in a slash, and the special method name 'default' is used to
handle multiple paths via a single handler. This layer also includes:
* the 'exposed' attribute (and cherrypy.expose)
* cherrypy.quickstart()
* _cp_config attributes
* cherrypy.tools (including cherrypy.session)
* cherrypy.url()
The ENVIRONMENT LAYER is used by developers at all levels. It provides
information about the current request and response, plus the application
and server environment, via a (default) set of top-level objects:
* cherrypy.request
* cherrypy.response
* cherrypy.engine
* cherrypy.server
* cherrypy.tree
* cherrypy.config
* cherrypy.thread_data
* cherrypy.log
* cherrypy.HTTPError, NotFound, and HTTPRedirect
* cherrypy.lib
The EXTENSION LAYER allows advanced users to construct and share their own
plugins. It consists of:
* Hook API
* Tool API
* Toolbox API
* Dispatch API
* Config Namespace API
Finally, there is the CORE LAYER, which uses the core API's to construct
the default components which are available at higher layers. You can think
of the default components as the 'reference implementation' for CherryPy.
Megaframeworks (and advanced users) may replace the default components
with customized or extended components. The core API's are:
* Application API
* Engine API
* Request API
* Server API
* WSGI API
These API's are described in the `CherryPy specification <https://bitbucket.org/cherrypy/cherrypy/wiki/CherryPySpec>`_.
"""
try:
import pkg_resources
except ImportError:
pass
from cherrypy._cperror import HTTPError, HTTPRedirect, InternalRedirect # noqa
from cherrypy._cperror import NotFound, CherryPyException, TimeoutError # noqa
from cherrypy import _cpdispatch as dispatch # noqa
from cherrypy import _cptools
tools = _cptools.default_toolbox
Tool = _cptools.Tool
from cherrypy import _cprequest
from cherrypy.lib import httputil as _httputil
from cherrypy import _cptree
tree = _cptree.Tree()
from cherrypy._cptree import Application # noqa
from cherrypy import _cpwsgi as wsgi # noqa
from cherrypy import process
try:
from cherrypy.process import win32
engine = win32.Win32Bus()
engine.console_control_handler = win32.ConsoleCtrlHandler(engine)
del win32
except ImportError:
engine = process.bus
try:
__version__ = pkg_resources.require('cherrypy')[0].version
except Exception:
__version__ = 'unknown'
# Timeout monitor. We add two channels to the engine
# to which cherrypy.Application will publish.
engine.listeners['before_request'] = set()
engine.listeners['after_request'] = set()
class _TimeoutMonitor(process.plugins.Monitor):
def __init__(self, bus):
self.servings = []
process.plugins.Monitor.__init__(self, bus, self.run)
def before_request(self):
self.servings.append((serving.request, serving.response))
def after_request(self):
try:
self.servings.remove((serving.request, serving.response))
except ValueError:
pass
def run(self):
"""Check timeout on all responses. (Internal)"""
for req, resp in self.servings:
resp.check_timeout()
engine.timeout_monitor = _TimeoutMonitor(engine)
engine.timeout_monitor.subscribe()
engine.autoreload = process.plugins.Autoreloader(engine)
engine.autoreload.subscribe()
engine.thread_manager = process.plugins.ThreadManager(engine)
engine.thread_manager.subscribe()
engine.signal_handler = process.plugins.SignalHandler(engine)
class _HandleSignalsPlugin(object):
"""Handle signals from other processes based on the configured
platform handlers above."""
def __init__(self, bus):
self.bus = bus
def subscribe(self):
"""Add the handlers based on the platform"""
if hasattr(self.bus, "signal_handler"):
self.bus.signal_handler.subscribe()
if hasattr(self.bus, "console_control_handler"):
self.bus.console_control_handler.subscribe()
engine.signals = _HandleSignalsPlugin(engine)
from cherrypy import _cpserver
server = _cpserver.Server()
server.subscribe()
def quickstart(root=None, script_name="", config=None):
"""Mount the given root, start the builtin server (and engine), then block.
root: an instance of a "controller class" (a collection of page handler
methods) which represents the root of the application.
script_name: a string containing the "mount point" of the application.
This should start with a slash, and be the path portion of the URL
at which to mount the given root. For example, if root.index() will
handle requests to "http://www.example.com:8080/dept/app1/", then
the script_name argument would be "/dept/app1".
It MUST NOT end in a slash. If the script_name refers to the root
of the URI, it MUST be an empty string (not "/").
config: a file or dict containing application config. If this contains
a [global] section, those entries will be used in the global
(site-wide) config.
"""
if config:
_global_conf_alias.update(config)
tree.mount(root, script_name, config)
engine.signals.subscribe()
engine.start()
engine.block()
from cherrypy._cpcompat import threadlocal as _local
class _Serving(_local):
"""An interface for registering request and response objects.
Rather than have a separate "thread local" object for the request and
the response, this class works as a single threadlocal container for
both objects (and any others which developers wish to define). In this
way, we can easily dump those objects when we stop/start a new HTTP
conversation, yet still refer to them as module-level globals in a
thread-safe way.
"""
request = _cprequest.Request(_httputil.Host("127.0.0.1", 80),
_httputil.Host("127.0.0.1", 1111))
"""
The request object for the current thread. In the main thread,
and any threads which are not receiving HTTP requests, this is None."""
response = _cprequest.Response()
"""
The response object for the current thread. In the main thread,
and any threads which are not receiving HTTP requests, this is None."""
def load(self, request, response):
self.request = request
self.response = response
def clear(self):
"""Remove all attributes of self."""
self.__dict__.clear()
serving = _Serving()
class _ThreadLocalProxy(object):
__slots__ = ['__attrname__', '__dict__']
def __init__(self, attrname):
self.__attrname__ = attrname
def __getattr__(self, name):
child = getattr(serving, self.__attrname__)
return getattr(child, name)
def __setattr__(self, name, value):
if name in ("__attrname__", ):
object.__setattr__(self, name, value)
else:
child = getattr(serving, self.__attrname__)
setattr(child, name, value)
def __delattr__(self, name):
child = getattr(serving, self.__attrname__)
delattr(child, name)
def _get_dict(self):
child = getattr(serving, self.__attrname__)
d = child.__class__.__dict__.copy()
d.update(child.__dict__)
return d
__dict__ = property(_get_dict)
def __getitem__(self, key):
child = getattr(serving, self.__attrname__)
return child[key]
def __setitem__(self, key, value):
child = getattr(serving, self.__attrname__)
child[key] = value
def __delitem__(self, key):
child = getattr(serving, self.__attrname__)
del child[key]
def __contains__(self, key):
child = getattr(serving, self.__attrname__)
return key in child
def __len__(self):
child = getattr(serving, self.__attrname__)
return len(child)
def __nonzero__(self):
child = getattr(serving, self.__attrname__)
return bool(child)
# Python 3
__bool__ = __nonzero__
# Create request and response object (the same objects will be used
# throughout the entire life of the webserver, but will redirect
# to the "serving" object)
request = _ThreadLocalProxy('request')
response = _ThreadLocalProxy('response')
# Create thread_data object as a thread-specific all-purpose storage
class _ThreadData(_local):
"""A container for thread-specific data."""
thread_data = _ThreadData()
# Monkeypatch pydoc to allow help() to go through the threadlocal proxy.
# Jan 2007: no Googleable examples of anyone else replacing pydoc.resolve.
# The only other way would be to change what is returned from type(request)
# and that's not possible in pure Python (you'd have to fake ob_type).
def _cherrypy_pydoc_resolve(thing, forceload=0):
"""Given an object or a path to an object, get the object and its name."""
if isinstance(thing, _ThreadLocalProxy):
thing = getattr(serving, thing.__attrname__)
return _pydoc._builtin_resolve(thing, forceload)
try:
import pydoc as _pydoc
_pydoc._builtin_resolve = _pydoc.resolve
_pydoc.resolve = _cherrypy_pydoc_resolve
except ImportError:
pass
from cherrypy import _cplogging
class _GlobalLogManager(_cplogging.LogManager):
"""A site-wide LogManager; routes to app.log or global log as appropriate.
This :class:`LogManager<cherrypy._cplogging.LogManager>` implements
cherrypy.log() and cherrypy.log.access(). If either
function is called during a request, the message will be sent to the
logger for the current Application. If they are called outside of a
request, the message will be sent to the site-wide logger.
"""
def __call__(self, *args, **kwargs):
"""Log the given message to the app.log or global log as appropriate.
"""
# Do NOT use try/except here. See
# https://github.com/cherrypy/cherrypy/issues/945
if hasattr(request, 'app') and hasattr(request.app, 'log'):
log = request.app.log
else:
log = self
return log.error(*args, **kwargs)
def access(self):
"""Log an access message to the app.log or global log as appropriate.
"""
try:
return request.app.log.access()
except AttributeError:
return _cplogging.LogManager.access(self)
log = _GlobalLogManager()
# Set a default screen handler on the global log.
log.screen = True
log.error_file = ''
# Using an access file makes CP about 10% slower. Leave off by default.
log.access_file = ''
def _buslog(msg, level):
log.error(msg, 'ENGINE', severity=level)
engine.subscribe('log', _buslog)
from cherrypy._helper import expose, popargs, url # noqa
# import _cpconfig last so it can reference other top-level objects
from cherrypy import _cpconfig
# Use _global_conf_alias so quickstart can use 'config' as an arg
# without shadowing cherrypy.config.
config = _global_conf_alias = _cpconfig.Config()
config.defaults = {
'tools.log_tracebacks.on': True,
'tools.log_headers.on': True,
'tools.trailing_slash.on': True,
'tools.encode.on': True
}
config.namespaces["log"] = lambda k, v: setattr(log, k, v)
config.namespaces["checker"] = lambda k, v: setattr(checker, k, v)
# Must reset to get our defaults applied.
config.reset()
from cherrypy import _cpchecker
checker = _cpchecker.Checker()
engine.subscribe('start', checker)

4
deps/cherrypy/__main__.py vendored Normal file
View File

@@ -0,0 +1,4 @@
import cherrypy.daemon
if __name__ == '__main__':
cherrypy.daemon.run()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

332
deps/cherrypy/_cpchecker.py vendored Normal file
View File

@@ -0,0 +1,332 @@
import os
import warnings
import cherrypy
from cherrypy._cpcompat import iteritems, copykeys, builtins
class Checker(object):
"""A checker for CherryPy sites and their mounted applications.
When this object is called at engine startup, it executes each
of its own methods whose names start with ``check_``. If you wish
to disable selected checks, simply add a line in your global
config which sets the appropriate method to False::
[global]
checker.check_skipped_app_config = False
You may also dynamically add or replace ``check_*`` methods in this way.
"""
on = True
"""If True (the default), run all checks; if False, turn off all checks."""
def __init__(self):
self._populate_known_types()
def __call__(self):
"""Run all check_* methods."""
if self.on:
oldformatwarning = warnings.formatwarning
warnings.formatwarning = self.formatwarning
try:
for name in dir(self):
if name.startswith("check_"):
method = getattr(self, name)
if method and hasattr(method, '__call__'):
method()
finally:
warnings.formatwarning = oldformatwarning
def formatwarning(self, message, category, filename, lineno, line=None):
"""Function to format a warning."""
return "CherryPy Checker:\n%s\n\n" % message
# This value should be set inside _cpconfig.
global_config_contained_paths = False
def check_app_config_entries_dont_start_with_script_name(self):
"""Check for Application config with sections that repeat script_name.
"""
for sn, app in cherrypy.tree.apps.items():
if not isinstance(app, cherrypy.Application):
continue
if not app.config:
continue
if sn == '':
continue
sn_atoms = sn.strip("/").split("/")
for key in app.config.keys():
key_atoms = key.strip("/").split("/")
if key_atoms[:len(sn_atoms)] == sn_atoms:
warnings.warn(
"The application mounted at %r has config "
"entries that start with its script name: %r" % (sn,
key))
def check_site_config_entries_in_app_config(self):
"""Check for mounted Applications that have site-scoped config."""
for sn, app in iteritems(cherrypy.tree.apps):
if not isinstance(app, cherrypy.Application):
continue
msg = []
for section, entries in iteritems(app.config):
if section.startswith('/'):
for key, value in iteritems(entries):
for n in ("engine.", "server.", "tree.", "checker."):
if key.startswith(n):
msg.append("[%s] %s = %s" %
(section, key, value))
if msg:
msg.insert(0,
"The application mounted at %r contains the "
"following config entries, which are only allowed "
"in site-wide config. Move them to a [global] "
"section and pass them to cherrypy.config.update() "
"instead of tree.mount()." % sn)
warnings.warn(os.linesep.join(msg))
def check_skipped_app_config(self):
"""Check for mounted Applications that have no config."""
for sn, app in cherrypy.tree.apps.items():
if not isinstance(app, cherrypy.Application):
continue
if not app.config:
msg = "The Application mounted at %r has an empty config." % sn
if self.global_config_contained_paths:
msg += (" It looks like the config you passed to "
"cherrypy.config.update() contains application-"
"specific sections. You must explicitly pass "
"application config via "
"cherrypy.tree.mount(..., config=app_config)")
warnings.warn(msg)
return
def check_app_config_brackets(self):
"""Check for Application config with extraneous brackets in section
names.
"""
for sn, app in cherrypy.tree.apps.items():
if not isinstance(app, cherrypy.Application):
continue
if not app.config:
continue
for key in app.config.keys():
if key.startswith("[") or key.endswith("]"):
warnings.warn(
"The application mounted at %r has config "
"section names with extraneous brackets: %r. "
"Config *files* need brackets; config *dicts* "
"(e.g. passed to tree.mount) do not." % (sn, key))
def check_static_paths(self):
"""Check Application config for incorrect static paths."""
# Use the dummy Request object in the main thread.
request = cherrypy.request
for sn, app in cherrypy.tree.apps.items():
if not isinstance(app, cherrypy.Application):
continue
request.app = app
for section in app.config:
# get_resource will populate request.config
request.get_resource(section + "/dummy.html")
conf = request.config.get
if conf("tools.staticdir.on", False):
msg = ""
root = conf("tools.staticdir.root")
dir = conf("tools.staticdir.dir")
if dir is None:
msg = "tools.staticdir.dir is not set."
else:
fulldir = ""
if os.path.isabs(dir):
fulldir = dir
if root:
msg = ("dir is an absolute path, even "
"though a root is provided.")
testdir = os.path.join(root, dir[1:])
if os.path.exists(testdir):
msg += (
"\nIf you meant to serve the "
"filesystem folder at %r, remove the "
"leading slash from dir." % (testdir,))
else:
if not root:
msg = (
"dir is a relative path and "
"no root provided.")
else:
fulldir = os.path.join(root, dir)
if not os.path.isabs(fulldir):
msg = ("%r is not an absolute path." % (
fulldir,))
if fulldir and not os.path.exists(fulldir):
if msg:
msg += "\n"
msg += ("%r (root + dir) is not an existing "
"filesystem path." % fulldir)
if msg:
warnings.warn("%s\nsection: [%s]\nroot: %r\ndir: %r"
% (msg, section, root, dir))
# -------------------------- Compatibility -------------------------- #
obsolete = {
'server.default_content_type': 'tools.response_headers.headers',
'log_access_file': 'log.access_file',
'log_config_options': None,
'log_file': 'log.error_file',
'log_file_not_found': None,
'log_request_headers': 'tools.log_headers.on',
'log_to_screen': 'log.screen',
'show_tracebacks': 'request.show_tracebacks',
'throw_errors': 'request.throw_errors',
'profiler.on': ('cherrypy.tree.mount(profiler.make_app('
'cherrypy.Application(Root())))'),
}
deprecated = {}
def _compat(self, config):
"""Process config and warn on each obsolete or deprecated entry."""
for section, conf in config.items():
if isinstance(conf, dict):
for k, v in conf.items():
if k in self.obsolete:
warnings.warn("%r is obsolete. Use %r instead.\n"
"section: [%s]" %
(k, self.obsolete[k], section))
elif k in self.deprecated:
warnings.warn("%r is deprecated. Use %r instead.\n"
"section: [%s]" %
(k, self.deprecated[k], section))
else:
if section in self.obsolete:
warnings.warn("%r is obsolete. Use %r instead."
% (section, self.obsolete[section]))
elif section in self.deprecated:
warnings.warn("%r is deprecated. Use %r instead."
% (section, self.deprecated[section]))
def check_compatibility(self):
"""Process config and warn on each obsolete or deprecated entry."""
self._compat(cherrypy.config)
for sn, app in cherrypy.tree.apps.items():
if not isinstance(app, cherrypy.Application):
continue
self._compat(app.config)
# ------------------------ Known Namespaces ------------------------ #
extra_config_namespaces = []
def _known_ns(self, app):
ns = ["wsgi"]
ns.extend(copykeys(app.toolboxes))
ns.extend(copykeys(app.namespaces))
ns.extend(copykeys(app.request_class.namespaces))
ns.extend(copykeys(cherrypy.config.namespaces))
ns += self.extra_config_namespaces
for section, conf in app.config.items():
is_path_section = section.startswith("/")
if is_path_section and isinstance(conf, dict):
for k, v in conf.items():
atoms = k.split(".")
if len(atoms) > 1:
if atoms[0] not in ns:
# Spit out a special warning if a known
# namespace is preceded by "cherrypy."
if atoms[0] == "cherrypy" and atoms[1] in ns:
msg = (
"The config entry %r is invalid; "
"try %r instead.\nsection: [%s]"
% (k, ".".join(atoms[1:]), section))
else:
msg = (
"The config entry %r is invalid, "
"because the %r config namespace "
"is unknown.\n"
"section: [%s]" % (k, atoms[0], section))
warnings.warn(msg)
elif atoms[0] == "tools":
if atoms[1] not in dir(cherrypy.tools):
msg = (
"The config entry %r may be invalid, "
"because the %r tool was not found.\n"
"section: [%s]" % (k, atoms[1], section))
warnings.warn(msg)
def check_config_namespaces(self):
"""Process config and warn on each unknown config namespace."""
for sn, app in cherrypy.tree.apps.items():
if not isinstance(app, cherrypy.Application):
continue
self._known_ns(app)
# -------------------------- Config Types -------------------------- #
known_config_types = {}
def _populate_known_types(self):
b = [x for x in vars(builtins).values()
if type(x) is type(str)]
def traverse(obj, namespace):
for name in dir(obj):
# Hack for 3.2's warning about body_params
if name == 'body_params':
continue
vtype = type(getattr(obj, name, None))
if vtype in b:
self.known_config_types[namespace + "." + name] = vtype
traverse(cherrypy.request, "request")
traverse(cherrypy.response, "response")
traverse(cherrypy.server, "server")
traverse(cherrypy.engine, "engine")
traverse(cherrypy.log, "log")
def _known_types(self, config):
msg = ("The config entry %r in section %r is of type %r, "
"which does not match the expected type %r.")
for section, conf in config.items():
if isinstance(conf, dict):
for k, v in conf.items():
if v is not None:
expected_type = self.known_config_types.get(k, None)
vtype = type(v)
if expected_type and vtype != expected_type:
warnings.warn(msg % (k, section, vtype.__name__,
expected_type.__name__))
else:
k, v = section, conf
if v is not None:
expected_type = self.known_config_types.get(k, None)
vtype = type(v)
if expected_type and vtype != expected_type:
warnings.warn(msg % (k, section, vtype.__name__,
expected_type.__name__))
def check_config_types(self):
"""Assert that config values are of the same type as default values."""
self._known_types(cherrypy.config)
for sn, app in cherrypy.tree.apps.items():
if not isinstance(app, cherrypy.Application):
continue
self._known_types(app.config)
# -------------------- Specific config warnings -------------------- #
def check_localhost(self):
"""Warn if any socket_host is 'localhost'. See #711."""
for k, v in cherrypy.config.items():
if k == 'server.socket_host' and v == 'localhost':
warnings.warn("The use of 'localhost' as a socket host can "
"cause problems on newer systems, since "
"'localhost' can map to either an IPv4 or an "
"IPv6 address. You should use '127.0.0.1' "
"or '[::1]' instead.")

330
deps/cherrypy/_cpcompat.py vendored Normal file
View File

@@ -0,0 +1,330 @@
"""Compatibility code for using CherryPy with various versions of Python.
CherryPy 3.2 is compatible with Python versions 2.6+. This module provides a
useful abstraction over the differences between Python versions, sometimes by
preferring a newer idiom, sometimes an older one, and sometimes a custom one.
In particular, Python 2 uses str and '' for byte strings, while Python 3
uses str and '' for unicode strings. We will call each of these the 'native
string' type for each version. Because of this major difference, this module
provides
two functions: 'ntob', which translates native strings (of type 'str') into
byte strings regardless of Python version, and 'ntou', which translates native
strings to unicode strings. This also provides a 'BytesIO' name for dealing
specifically with bytes, and a 'StringIO' name for dealing with native strings.
It also provides a 'base64_decode' function with native strings as input and
output.
"""
import os
import re
import sys
import threading
import six
if six.PY3:
def ntob(n, encoding='ISO-8859-1'):
"""Return the given native string as a byte string in the given
encoding.
"""
assert_native(n)
# In Python 3, the native string type is unicode
return n.encode(encoding)
def ntou(n, encoding='ISO-8859-1'):
"""Return the given native string as a unicode string with the given
encoding.
"""
assert_native(n)
# In Python 3, the native string type is unicode
return n
def tonative(n, encoding='ISO-8859-1'):
"""Return the given string as a native string in the given encoding."""
# In Python 3, the native string type is unicode
if isinstance(n, bytes):
return n.decode(encoding)
return n
else:
# Python 2
def ntob(n, encoding='ISO-8859-1'):
"""Return the given native string as a byte string in the given
encoding.
"""
assert_native(n)
# In Python 2, the native string type is bytes. Assume it's already
# in the given encoding, which for ISO-8859-1 is almost always what
# was intended.
return n
def ntou(n, encoding='ISO-8859-1'):
"""Return the given native string as a unicode string with the given
encoding.
"""
assert_native(n)
# In Python 2, the native string type is bytes.
# First, check for the special encoding 'escape'. The test suite uses
# this to signal that it wants to pass a string with embedded \uXXXX
# escapes, but without having to prefix it with u'' for Python 2,
# but no prefix for Python 3.
if encoding == 'escape':
return unicode(
re.sub(r'\\u([0-9a-zA-Z]{4})',
lambda m: unichr(int(m.group(1), 16)),
n.decode('ISO-8859-1')))
# Assume it's already in the given encoding, which for ISO-8859-1
# is almost always what was intended.
return n.decode(encoding)
def tonative(n, encoding='ISO-8859-1'):
"""Return the given string as a native string in the given encoding."""
# In Python 2, the native string type is bytes.
if isinstance(n, unicode):
return n.encode(encoding)
return n
def assert_native(n):
if not isinstance(n, str):
raise TypeError("n must be a native str (got %s)" % type(n).__name__)
try:
# Python 3.1+
from base64 import decodebytes as _base64_decodebytes
except ImportError:
# Python 3.0-
# since CherryPy claims compability with Python 2.3, we must use
# the legacy API of base64
from base64 import decodestring as _base64_decodebytes
def base64_decode(n, encoding='ISO-8859-1'):
"""Return the native string base64-decoded (as a native string)."""
if isinstance(n, six.text_type):
b = n.encode(encoding)
else:
b = n
b = _base64_decodebytes(b)
if str is six.text_type:
return b.decode(encoding)
else:
return b
try:
sorted = sorted
except NameError:
def sorted(i):
i = i[:]
i.sort()
return i
try:
reversed = reversed
except NameError:
def reversed(x):
i = len(x)
while i > 0:
i -= 1
yield x[i]
try:
# Python 3
from urllib.parse import urljoin, urlencode
from urllib.parse import quote, quote_plus
from urllib.request import unquote, urlopen
from urllib.request import parse_http_list, parse_keqv_list
except ImportError:
# Python 2
from urlparse import urljoin # noqa
from urllib import urlencode, urlopen # noqa
from urllib import quote, quote_plus # noqa
from urllib import unquote # noqa
from urllib2 import parse_http_list, parse_keqv_list # noqa
try:
from threading import local as threadlocal
except ImportError:
from cherrypy._cpthreadinglocal import local as threadlocal # noqa
try:
dict.iteritems
# Python 2
iteritems = lambda d: d.iteritems()
copyitems = lambda d: d.items()
except AttributeError:
# Python 3
iteritems = lambda d: d.items()
copyitems = lambda d: list(d.items())
try:
dict.iterkeys
# Python 2
iterkeys = lambda d: d.iterkeys()
copykeys = lambda d: d.keys()
except AttributeError:
# Python 3
iterkeys = lambda d: d.keys()
copykeys = lambda d: list(d.keys())
try:
dict.itervalues
# Python 2
itervalues = lambda d: d.itervalues()
copyvalues = lambda d: d.values()
except AttributeError:
# Python 3
itervalues = lambda d: d.values()
copyvalues = lambda d: list(d.values())
try:
# Python 3
import builtins
except ImportError:
# Python 2
import __builtin__ as builtins # noqa
try:
# Python 2. We try Python 2 first clients on Python 2
# don't try to import the 'http' module from cherrypy.lib
from Cookie import SimpleCookie, CookieError
from httplib import BadStatusLine, HTTPConnection, IncompleteRead
from httplib import NotConnected
from BaseHTTPServer import BaseHTTPRequestHandler
except ImportError:
# Python 3
from http.cookies import SimpleCookie, CookieError # noqa
from http.client import BadStatusLine, HTTPConnection, IncompleteRead # noqa
from http.client import NotConnected # noqa
from http.server import BaseHTTPRequestHandler # noqa
# Some platforms don't expose HTTPSConnection, so handle it separately
if six.PY3:
try:
from http.client import HTTPSConnection
except ImportError:
# Some platforms which don't have SSL don't expose HTTPSConnection
HTTPSConnection = None
else:
try:
from httplib import HTTPSConnection
except ImportError:
HTTPSConnection = None
try:
# Python 2
xrange = xrange
except NameError:
# Python 3
xrange = range
import threading
if hasattr(threading.Thread, "daemon"):
# Python 2.6+
def get_daemon(t):
return t.daemon
def set_daemon(t, val):
t.daemon = val
else:
def get_daemon(t):
return t.isDaemon()
def set_daemon(t, val):
t.setDaemon(val)
try:
# Python 3
from urllib.parse import unquote as parse_unquote
def unquote_qs(atom, encoding, errors='strict'):
return parse_unquote(
atom.replace('+', ' '),
encoding=encoding,
errors=errors)
except ImportError:
# Python 2
from urllib import unquote as parse_unquote
def unquote_qs(atom, encoding, errors='strict'):
return parse_unquote(atom.replace('+', ' ')).decode(encoding, errors)
try:
# Prefer simplejson, which is usually more advanced than the builtin
# module.
import simplejson as json
json_decode = json.JSONDecoder().decode
_json_encode = json.JSONEncoder().iterencode
except ImportError:
if sys.version_info >= (2, 6):
# Python >=2.6 : json is part of the standard library
import json
json_decode = json.JSONDecoder().decode
_json_encode = json.JSONEncoder().iterencode
else:
json = None
def json_decode(s):
raise ValueError('No JSON library is available')
def _json_encode(s):
raise ValueError('No JSON library is available')
finally:
if json and six.PY3:
# The two Python 3 implementations (simplejson/json)
# outputs str. We need bytes.
def json_encode(value):
for chunk in _json_encode(value):
yield chunk.encode('utf8')
else:
json_encode = _json_encode
text_or_bytes = six.text_type, six.binary_type
try:
import cPickle as pickle
except ImportError:
# In Python 2, pickle is a Python version.
# In Python 3, pickle is the sped-up C version.
import pickle # noqa
import binascii
def random20():
return binascii.hexlify(os.urandom(20)).decode('ascii')
try:
from _thread import get_ident as get_thread_ident
except ImportError:
from thread import get_ident as get_thread_ident # noqa
try:
# Python 3
next = next
except NameError:
# Python 2
def next(i):
return i.next()
if sys.version_info >= (3, 3):
Timer = threading.Timer
Event = threading.Event
else:
# Python 3.2 and earlier
Timer = threading._Timer
Event = threading._Event
# Prior to Python 2.6, the Thread class did not have a .daemon property.
# This mix-in adds that property.
class SetDaemonProperty:
def __get_daemon(self):
return self.isDaemon()
def __set_daemon(self, daemon):
self.setDaemon(daemon)
if sys.version_info < (2, 6):
daemon = property(__get_daemon, __set_daemon)

303
deps/cherrypy/_cpconfig.py vendored Normal file
View File

@@ -0,0 +1,303 @@
"""
Configuration system for CherryPy.
Configuration in CherryPy is implemented via dictionaries. Keys are strings
which name the mapped value, which may be of any type.
Architecture
------------
CherryPy Requests are part of an Application, which runs in a global context,
and configuration data may apply to any of those three scopes:
Global
Configuration entries which apply everywhere are stored in
cherrypy.config.
Application
Entries which apply to each mounted application are stored
on the Application object itself, as 'app.config'. This is a two-level
dict where each key is a path, or "relative URL" (for example, "/" or
"/path/to/my/page"), and each value is a config dict. Usually, this
data is provided in the call to tree.mount(root(), config=conf),
although you may also use app.merge(conf).
Request
Each Request object possesses a single 'Request.config' dict.
Early in the request process, this dict is populated by merging global
config entries, Application entries (whose path equals or is a parent
of Request.path_info), and any config acquired while looking up the
page handler (see next).
Declaration
-----------
Configuration data may be supplied as a Python dictionary, as a filename,
or as an open file object. When you supply a filename or file, CherryPy
uses Python's builtin ConfigParser; you declare Application config by
writing each path as a section header::
[/path/to/my/page]
request.stream = True
To declare global configuration entries, place them in a [global] section.
You may also declare config entries directly on the classes and methods
(page handlers) that make up your CherryPy application via the ``_cp_config``
attribute, set with the ``cherrypy.config`` decorator. For example::
@cherrypy.config(**{'tools.gzip.on': True})
class Demo:
@cherrypy.expose
@cherrypy.config(**{'request.show_tracebacks': False})
def index(self):
return "Hello world"
.. note::
This behavior is only guaranteed for the default dispatcher.
Other dispatchers may have different restrictions on where
you can attach config attributes.
Namespaces
----------
Configuration keys are separated into namespaces by the first "." in the key.
Current namespaces:
engine
Controls the 'application engine', including autoreload.
These can only be declared in the global config.
tree
Grafts cherrypy.Application objects onto cherrypy.tree.
These can only be declared in the global config.
hooks
Declares additional request-processing functions.
log
Configures the logging for each application.
These can only be declared in the global or / config.
request
Adds attributes to each Request.
response
Adds attributes to each Response.
server
Controls the default HTTP server via cherrypy.server.
These can only be declared in the global config.
tools
Runs and configures additional request-processing packages.
wsgi
Adds WSGI middleware to an Application's "pipeline".
These can only be declared in the app's root config ("/").
checker
Controls the 'checker', which looks for common errors in
app state (including config) when the engine starts.
Global config only.
The only key that does not exist in a namespace is the "environment" entry.
This special entry 'imports' other config entries from a template stored in
cherrypy._cpconfig.environments[environment]. It only applies to the global
config, and only when you use cherrypy.config.update.
You can define your own namespaces to be called at the Global, Application,
or Request level, by adding a named handler to cherrypy.config.namespaces,
app.namespaces, or app.request_class.namespaces. The name can
be any string, and the handler must be either a callable or a (Python 2.5
style) context manager.
"""
import cherrypy
from cherrypy._cpcompat import text_or_bytes
from cherrypy.lib import reprconf
# Deprecated in CherryPy 3.2--remove in 3.3
NamespaceSet = reprconf.NamespaceSet
def merge(base, other):
"""Merge one app config (from a dict, file, or filename) into another.
If the given config is a filename, it will be appended to
the list of files to monitor for "autoreload" changes.
"""
if isinstance(other, text_or_bytes):
cherrypy.engine.autoreload.files.add(other)
# Load other into base
for section, value_map in reprconf.as_dict(other).items():
if not isinstance(value_map, dict):
raise ValueError(
"Application config must include section headers, but the "
"config you tried to merge doesn't have any sections. "
"Wrap your config in another dict with paths as section "
"headers, for example: {'/': config}.")
base.setdefault(section, {}).update(value_map)
class Config(reprconf.Config):
"""The 'global' configuration data for the entire CherryPy process."""
def update(self, config):
"""Update self from a dict, file or filename."""
if isinstance(config, text_or_bytes):
# Filename
cherrypy.engine.autoreload.files.add(config)
reprconf.Config.update(self, config)
def _apply(self, config):
"""Update self from a dict."""
if isinstance(config.get("global"), dict):
if len(config) > 1:
cherrypy.checker.global_config_contained_paths = True
config = config["global"]
if 'tools.staticdir.dir' in config:
config['tools.staticdir.section'] = "global"
reprconf.Config._apply(self, config)
@staticmethod
def __call__(*args, **kwargs):
"""Decorator for page handlers to set _cp_config."""
if args:
raise TypeError(
"The cherrypy.config decorator does not accept positional "
"arguments; you must use keyword arguments.")
def tool_decorator(f):
_Vars(f).setdefault('_cp_config', {}).update(kwargs)
return f
return tool_decorator
class _Vars(object):
"""
Adapter that allows setting a default attribute on a function
or class.
"""
def __init__(self, target):
self.target = target
def setdefault(self, key, default):
if not hasattr(self.target, key):
setattr(self.target, key, default)
return getattr(self.target, key)
# Sphinx begin config.environments
Config.environments = environments = {
"staging": {
'engine.autoreload.on': False,
'checker.on': False,
'tools.log_headers.on': False,
'request.show_tracebacks': False,
'request.show_mismatched_params': False,
},
"production": {
'engine.autoreload.on': False,
'checker.on': False,
'tools.log_headers.on': False,
'request.show_tracebacks': False,
'request.show_mismatched_params': False,
'log.screen': False,
},
"embedded": {
# For use with CherryPy embedded in another deployment stack.
'engine.autoreload.on': False,
'checker.on': False,
'tools.log_headers.on': False,
'request.show_tracebacks': False,
'request.show_mismatched_params': False,
'log.screen': False,
'engine.SIGHUP': None,
'engine.SIGTERM': None,
},
"test_suite": {
'engine.autoreload.on': False,
'checker.on': False,
'tools.log_headers.on': False,
'request.show_tracebacks': True,
'request.show_mismatched_params': True,
'log.screen': False,
},
}
# Sphinx end config.environments
def _server_namespace_handler(k, v):
"""Config handler for the "server" namespace."""
atoms = k.split(".", 1)
if len(atoms) > 1:
# Special-case config keys of the form 'server.servername.socket_port'
# to configure additional HTTP servers.
if not hasattr(cherrypy, "servers"):
cherrypy.servers = {}
servername, k = atoms
if servername not in cherrypy.servers:
from cherrypy import _cpserver
cherrypy.servers[servername] = _cpserver.Server()
# On by default, but 'on = False' can unsubscribe it (see below).
cherrypy.servers[servername].subscribe()
if k == 'on':
if v:
cherrypy.servers[servername].subscribe()
else:
cherrypy.servers[servername].unsubscribe()
else:
setattr(cherrypy.servers[servername], k, v)
else:
setattr(cherrypy.server, k, v)
Config.namespaces["server"] = _server_namespace_handler
def _engine_namespace_handler(k, v):
"""Config handler for the "engine" namespace."""
engine = cherrypy.engine
if k == 'SIGHUP':
engine.subscribe('SIGHUP', v)
elif k == 'SIGTERM':
engine.subscribe('SIGTERM', v)
elif "." in k:
plugin, attrname = k.split(".", 1)
plugin = getattr(engine, plugin)
if attrname == 'on':
if v and hasattr(getattr(plugin, 'subscribe', None), '__call__'):
plugin.subscribe()
return
elif (
(not v) and
hasattr(getattr(plugin, 'unsubscribe', None), '__call__')
):
plugin.unsubscribe()
return
setattr(plugin, attrname, v)
else:
setattr(engine, k, v)
Config.namespaces["engine"] = _engine_namespace_handler
def _tree_namespace_handler(k, v):
"""Namespace handler for the 'tree' config namespace."""
if isinstance(v, dict):
for script_name, app in v.items():
cherrypy.tree.graft(app, script_name)
msg = "Mounted: %s on %s" % (app, script_name or "/")
cherrypy.engine.log(msg)
else:
cherrypy.tree.graft(v, v.script_name)
cherrypy.engine.log("Mounted: %s on %s" % (v, v.script_name or "/"))
Config.namespaces["tree"] = _tree_namespace_handler

685
deps/cherrypy/_cpdispatch.py vendored Normal file
View File

@@ -0,0 +1,685 @@
"""CherryPy dispatchers.
A 'dispatcher' is the object which looks up the 'page handler' callable
and collects config for the current request based on the path_info, other
request attributes, and the application architecture. The core calls the
dispatcher as early as possible, passing it a 'path_info' argument.
The default dispatcher discovers the page handler by matching path_info
to a hierarchical arrangement of objects, starting at request.app.root.
"""
import string
import sys
import types
try:
classtype = (type, types.ClassType)
except AttributeError:
classtype = type
import cherrypy
class PageHandler(object):
"""Callable which sets response.body."""
def __init__(self, callable, *args, **kwargs):
self.callable = callable
self.args = args
self.kwargs = kwargs
def get_args(self):
return cherrypy.serving.request.args
def set_args(self, args):
cherrypy.serving.request.args = args
return cherrypy.serving.request.args
args = property(
get_args,
set_args,
doc="The ordered args should be accessible from post dispatch hooks"
)
def get_kwargs(self):
return cherrypy.serving.request.kwargs
def set_kwargs(self, kwargs):
cherrypy.serving.request.kwargs = kwargs
return cherrypy.serving.request.kwargs
kwargs = property(
get_kwargs,
set_kwargs,
doc="The named kwargs should be accessible from post dispatch hooks"
)
def __call__(self):
try:
return self.callable(*self.args, **self.kwargs)
except TypeError:
x = sys.exc_info()[1]
try:
test_callable_spec(self.callable, self.args, self.kwargs)
except cherrypy.HTTPError:
raise sys.exc_info()[1]
except:
raise x
raise
def test_callable_spec(callable, callable_args, callable_kwargs):
"""
Inspect callable and test to see if the given args are suitable for it.
When an error occurs during the handler's invoking stage there are 2
erroneous cases:
1. Too many parameters passed to a function which doesn't define
one of *args or **kwargs.
2. Too little parameters are passed to the function.
There are 3 sources of parameters to a cherrypy handler.
1. query string parameters are passed as keyword parameters to the
handler.
2. body parameters are also passed as keyword parameters.
3. when partial matching occurs, the final path atoms are passed as
positional args.
Both the query string and path atoms are part of the URI. If they are
incorrect, then a 404 Not Found should be raised. Conversely the body
parameters are part of the request; if they are invalid a 400 Bad Request.
"""
show_mismatched_params = getattr(
cherrypy.serving.request, 'show_mismatched_params', False)
try:
(args, varargs, varkw, defaults) = getargspec(callable)
except TypeError:
if isinstance(callable, object) and hasattr(callable, '__call__'):
(args, varargs, varkw,
defaults) = getargspec(callable.__call__)
else:
# If it wasn't one of our own types, re-raise
# the original error
raise
if args and args[0] == 'self':
args = args[1:]
arg_usage = dict([(arg, 0,) for arg in args])
vararg_usage = 0
varkw_usage = 0
extra_kwargs = set()
for i, value in enumerate(callable_args):
try:
arg_usage[args[i]] += 1
except IndexError:
vararg_usage += 1
for key in callable_kwargs.keys():
try:
arg_usage[key] += 1
except KeyError:
varkw_usage += 1
extra_kwargs.add(key)
# figure out which args have defaults.
args_with_defaults = args[-len(defaults or []):]
for i, val in enumerate(defaults or []):
# Defaults take effect only when the arg hasn't been used yet.
if arg_usage[args_with_defaults[i]] == 0:
arg_usage[args_with_defaults[i]] += 1
missing_args = []
multiple_args = []
for key, usage in arg_usage.items():
if usage == 0:
missing_args.append(key)
elif usage > 1:
multiple_args.append(key)
if missing_args:
# In the case where the method allows body arguments
# there are 3 potential errors:
# 1. not enough query string parameters -> 404
# 2. not enough body parameters -> 400
# 3. not enough path parts (partial matches) -> 404
#
# We can't actually tell which case it is,
# so I'm raising a 404 because that covers 2/3 of the
# possibilities
#
# In the case where the method does not allow body
# arguments it's definitely a 404.
message = None
if show_mismatched_params:
message = "Missing parameters: %s" % ",".join(missing_args)
raise cherrypy.HTTPError(404, message=message)
# the extra positional arguments come from the path - 404 Not Found
if not varargs and vararg_usage > 0:
raise cherrypy.HTTPError(404)
body_params = cherrypy.serving.request.body.params or {}
body_params = set(body_params.keys())
qs_params = set(callable_kwargs.keys()) - body_params
if multiple_args:
if qs_params.intersection(set(multiple_args)):
# If any of the multiple parameters came from the query string then
# it's a 404 Not Found
error = 404
else:
# Otherwise it's a 400 Bad Request
error = 400
message = None
if show_mismatched_params:
message = "Multiple values for parameters: "\
"%s" % ",".join(multiple_args)
raise cherrypy.HTTPError(error, message=message)
if not varkw and varkw_usage > 0:
# If there were extra query string parameters, it's a 404 Not Found
extra_qs_params = set(qs_params).intersection(extra_kwargs)
if extra_qs_params:
message = None
if show_mismatched_params:
message = "Unexpected query string "\
"parameters: %s" % ", ".join(extra_qs_params)
raise cherrypy.HTTPError(404, message=message)
# If there were any extra body parameters, it's a 400 Not Found
extra_body_params = set(body_params).intersection(extra_kwargs)
if extra_body_params:
message = None
if show_mismatched_params:
message = "Unexpected body parameters: "\
"%s" % ", ".join(extra_body_params)
raise cherrypy.HTTPError(400, message=message)
try:
import inspect
except ImportError:
test_callable_spec = lambda callable, args, kwargs: None
else:
getargspec = inspect.getargspec
# Python 3 requires using getfullargspec if keyword-only arguments are present
if hasattr(inspect, 'getfullargspec'):
def getargspec(callable):
return inspect.getfullargspec(callable)[:4]
class LateParamPageHandler(PageHandler):
"""When passing cherrypy.request.params to the page handler, we do not
want to capture that dict too early; we want to give tools like the
decoding tool a chance to modify the params dict in-between the lookup
of the handler and the actual calling of the handler. This subclass
takes that into account, and allows request.params to be 'bound late'
(it's more complicated than that, but that's the effect).
"""
def _get_kwargs(self):
kwargs = cherrypy.serving.request.params.copy()
if self._kwargs:
kwargs.update(self._kwargs)
return kwargs
def _set_kwargs(self, kwargs):
cherrypy.serving.request.kwargs = kwargs
self._kwargs = kwargs
kwargs = property(_get_kwargs, _set_kwargs,
doc='page handler kwargs (with '
'cherrypy.request.params copied in)')
if sys.version_info < (3, 0):
punctuation_to_underscores = string.maketrans(
string.punctuation, '_' * len(string.punctuation))
def validate_translator(t):
if not isinstance(t, str) or len(t) != 256:
raise ValueError(
"The translate argument must be a str of len 256.")
else:
punctuation_to_underscores = str.maketrans(
string.punctuation, '_' * len(string.punctuation))
def validate_translator(t):
if not isinstance(t, dict):
raise ValueError("The translate argument must be a dict.")
class Dispatcher(object):
"""CherryPy Dispatcher which walks a tree of objects to find a handler.
The tree is rooted at cherrypy.request.app.root, and each hierarchical
component in the path_info argument is matched to a corresponding nested
attribute of the root object. Matching handlers must have an 'exposed'
attribute which evaluates to True. The special method name "index"
matches a URI which ends in a slash ("/"). The special method name
"default" may match a portion of the path_info (but only when no longer
substring of the path_info matches some other object).
This is the default, built-in dispatcher for CherryPy.
"""
dispatch_method_name = '_cp_dispatch'
"""
The name of the dispatch method that nodes may optionally implement
to provide their own dynamic dispatch algorithm.
"""
def __init__(self, dispatch_method_name=None,
translate=punctuation_to_underscores):
validate_translator(translate)
self.translate = translate
if dispatch_method_name:
self.dispatch_method_name = dispatch_method_name
def __call__(self, path_info):
"""Set handler and config for the current request."""
request = cherrypy.serving.request
func, vpath = self.find_handler(path_info)
if func:
# Decode any leftover %2F in the virtual_path atoms.
vpath = [x.replace("%2F", "/") for x in vpath]
request.handler = LateParamPageHandler(func, *vpath)
else:
request.handler = cherrypy.NotFound()
def find_handler(self, path):
"""Return the appropriate page handler, plus any virtual path.
This will return two objects. The first will be a callable,
which can be used to generate page output. Any parameters from
the query string or request body will be sent to that callable
as keyword arguments.
The callable is found by traversing the application's tree,
starting from cherrypy.request.app.root, and matching path
components to successive objects in the tree. For example, the
URL "/path/to/handler" might return root.path.to.handler.
The second object returned will be a list of names which are
'virtual path' components: parts of the URL which are dynamic,
and were not used when looking up the handler.
These virtual path components are passed to the handler as
positional arguments.
"""
request = cherrypy.serving.request
app = request.app
root = app.root
dispatch_name = self.dispatch_method_name
# Get config for the root object/path.
fullpath = [x for x in path.strip('/').split('/') if x] + ['index']
fullpath_len = len(fullpath)
segleft = fullpath_len
nodeconf = {}
if hasattr(root, "_cp_config"):
nodeconf.update(root._cp_config)
if "/" in app.config:
nodeconf.update(app.config["/"])
object_trail = [['root', root, nodeconf, segleft]]
node = root
iternames = fullpath[:]
while iternames:
name = iternames[0]
# map to legal Python identifiers (e.g. replace '.' with '_')
objname = name.translate(self.translate)
nodeconf = {}
subnode = getattr(node, objname, None)
pre_len = len(iternames)
if subnode is None:
dispatch = getattr(node, dispatch_name, None)
if dispatch and hasattr(dispatch, '__call__') and not \
getattr(dispatch, 'exposed', False) and \
pre_len > 1:
# Don't expose the hidden 'index' token to _cp_dispatch
# We skip this if pre_len == 1 since it makes no sense
# to call a dispatcher when we have no tokens left.
index_name = iternames.pop()
subnode = dispatch(vpath=iternames)
iternames.append(index_name)
else:
# We didn't find a path, but keep processing in case there
# is a default() handler.
iternames.pop(0)
else:
# We found the path, remove the vpath entry
iternames.pop(0)
segleft = len(iternames)
if segleft > pre_len:
# No path segment was removed. Raise an error.
raise cherrypy.CherryPyException(
"A vpath segment was added. Custom dispatchers may only "
+ "remove elements. While trying to process "
+ "{0} in {1}".format(name, fullpath)
)
elif segleft == pre_len:
# Assume that the handler used the current path segment, but
# did not pop it. This allows things like
# return getattr(self, vpath[0], None)
iternames.pop(0)
segleft -= 1
node = subnode
if node is not None:
# Get _cp_config attached to this node.
if hasattr(node, "_cp_config"):
nodeconf.update(node._cp_config)
# Mix in values from app.config for this path.
existing_len = fullpath_len - pre_len
if existing_len != 0:
curpath = '/' + '/'.join(fullpath[0:existing_len])
else:
curpath = ''
new_segs = fullpath[fullpath_len - pre_len:fullpath_len - segleft]
for seg in new_segs:
curpath += '/' + seg
if curpath in app.config:
nodeconf.update(app.config[curpath])
object_trail.append([name, node, nodeconf, segleft])
def set_conf():
"""Collapse all object_trail config into cherrypy.request.config.
"""
base = cherrypy.config.copy()
# Note that we merge the config from each node
# even if that node was None.
for name, obj, conf, segleft in object_trail:
base.update(conf)
if 'tools.staticdir.dir' in conf:
base['tools.staticdir.section'] = '/' + \
'/'.join(fullpath[0:fullpath_len - segleft])
return base
# Try successive objects (reverse order)
num_candidates = len(object_trail) - 1
for i in range(num_candidates, -1, -1):
name, candidate, nodeconf, segleft = object_trail[i]
if candidate is None:
continue
# Try a "default" method on the current leaf.
if hasattr(candidate, "default"):
defhandler = candidate.default
if getattr(defhandler, 'exposed', False):
# Insert any extra _cp_config from the default handler.
conf = getattr(defhandler, "_cp_config", {})
object_trail.insert(
i + 1, ["default", defhandler, conf, segleft])
request.config = set_conf()
# See https://github.com/cherrypy/cherrypy/issues/613
request.is_index = path.endswith("/")
return defhandler, fullpath[fullpath_len - segleft:-1]
# Uncomment the next line to restrict positional params to
# "default".
# if i < num_candidates - 2: continue
# Try the current leaf.
if getattr(candidate, 'exposed', False):
request.config = set_conf()
if i == num_candidates:
# We found the extra ".index". Mark request so tools
# can redirect if path_info has no trailing slash.
request.is_index = True
else:
# We're not at an 'index' handler. Mark request so tools
# can redirect if path_info has NO trailing slash.
# Note that this also includes handlers which take
# positional parameters (virtual paths).
request.is_index = False
return candidate, fullpath[fullpath_len - segleft:-1]
# We didn't find anything
request.config = set_conf()
return None, []
class MethodDispatcher(Dispatcher):
"""Additional dispatch based on cherrypy.request.method.upper().
Methods named GET, POST, etc will be called on an exposed class.
The method names must be all caps; the appropriate Allow header
will be output showing all capitalized method names as allowable
HTTP verbs.
Note that the containing class must be exposed, not the methods.
"""
def __call__(self, path_info):
"""Set handler and config for the current request."""
request = cherrypy.serving.request
resource, vpath = self.find_handler(path_info)
if resource:
# Set Allow header
avail = [m for m in dir(resource) if m.isupper()]
if "GET" in avail and "HEAD" not in avail:
avail.append("HEAD")
avail.sort()
cherrypy.serving.response.headers['Allow'] = ", ".join(avail)
# Find the subhandler
meth = request.method.upper()
func = getattr(resource, meth, None)
if func is None and meth == "HEAD":
func = getattr(resource, "GET", None)
if func:
# Grab any _cp_config on the subhandler.
if hasattr(func, "_cp_config"):
request.config.update(func._cp_config)
# Decode any leftover %2F in the virtual_path atoms.
vpath = [x.replace("%2F", "/") for x in vpath]
request.handler = LateParamPageHandler(func, *vpath)
else:
request.handler = cherrypy.HTTPError(405)
else:
request.handler = cherrypy.NotFound()
class RoutesDispatcher(object):
"""A Routes based dispatcher for CherryPy."""
def __init__(self, full_result=False, **mapper_options):
"""
Routes dispatcher
Set full_result to True if you wish the controller
and the action to be passed on to the page handler
parameters. By default they won't be.
"""
import routes
self.full_result = full_result
self.controllers = {}
self.mapper = routes.Mapper(**mapper_options)
self.mapper.controller_scan = self.controllers.keys
def connect(self, name, route, controller, **kwargs):
self.controllers[name] = controller
self.mapper.connect(name, route, controller=name, **kwargs)
def redirect(self, url):
raise cherrypy.HTTPRedirect(url)
def __call__(self, path_info):
"""Set handler and config for the current request."""
func = self.find_handler(path_info)
if func:
cherrypy.serving.request.handler = LateParamPageHandler(func)
else:
cherrypy.serving.request.handler = cherrypy.NotFound()
def find_handler(self, path_info):
"""Find the right page handler, and set request.config."""
import routes
request = cherrypy.serving.request
config = routes.request_config()
config.mapper = self.mapper
if hasattr(request, 'wsgi_environ'):
config.environ = request.wsgi_environ
config.host = request.headers.get('Host', None)
config.protocol = request.scheme
config.redirect = self.redirect
result = self.mapper.match(path_info)
config.mapper_dict = result
params = {}
if result:
params = result.copy()
if not self.full_result:
params.pop('controller', None)
params.pop('action', None)
request.params.update(params)
# Get config for the root object/path.
request.config = base = cherrypy.config.copy()
curpath = ""
def merge(nodeconf):
if 'tools.staticdir.dir' in nodeconf:
nodeconf['tools.staticdir.section'] = curpath or "/"
base.update(nodeconf)
app = request.app
root = app.root
if hasattr(root, "_cp_config"):
merge(root._cp_config)
if "/" in app.config:
merge(app.config["/"])
# Mix in values from app.config.
atoms = [x for x in path_info.split("/") if x]
if atoms:
last = atoms.pop()
else:
last = None
for atom in atoms:
curpath = "/".join((curpath, atom))
if curpath in app.config:
merge(app.config[curpath])
handler = None
if result:
controller = result.get('controller')
controller = self.controllers.get(controller, controller)
if controller:
if isinstance(controller, classtype):
controller = controller()
# Get config from the controller.
if hasattr(controller, "_cp_config"):
merge(controller._cp_config)
action = result.get('action')
if action is not None:
handler = getattr(controller, action, None)
# Get config from the handler
if hasattr(handler, "_cp_config"):
merge(handler._cp_config)
else:
handler = controller
# Do the last path atom here so it can
# override the controller's _cp_config.
if last:
curpath = "/".join((curpath, last))
if curpath in app.config:
merge(app.config[curpath])
return handler
def XMLRPCDispatcher(next_dispatcher=Dispatcher()):
from cherrypy.lib import xmlrpcutil
def xmlrpc_dispatch(path_info):
path_info = xmlrpcutil.patched_path(path_info)
return next_dispatcher(path_info)
return xmlrpc_dispatch
def VirtualHost(next_dispatcher=Dispatcher(), use_x_forwarded_host=True,
**domains):
"""
Select a different handler based on the Host header.
This can be useful when running multiple sites within one CP server.
It allows several domains to point to different parts of a single
website structure. For example::
http://www.domain.example -> root
http://www.domain2.example -> root/domain2/
http://www.domain2.example:443 -> root/secure
can be accomplished via the following config::
[/]
request.dispatch = cherrypy.dispatch.VirtualHost(
**{'www.domain2.example': '/domain2',
'www.domain2.example:443': '/secure',
})
next_dispatcher
The next dispatcher object in the dispatch chain.
The VirtualHost dispatcher adds a prefix to the URL and calls
another dispatcher. Defaults to cherrypy.dispatch.Dispatcher().
use_x_forwarded_host
If True (the default), any "X-Forwarded-Host"
request header will be used instead of the "Host" header. This
is commonly added by HTTP servers (such as Apache) when proxying.
``**domains``
A dict of {host header value: virtual prefix} pairs.
The incoming "Host" request header is looked up in this dict,
and, if a match is found, the corresponding "virtual prefix"
value will be prepended to the URL path before calling the
next dispatcher. Note that you often need separate entries
for "example.com" and "www.example.com". In addition, "Host"
headers may contain the port number.
"""
from cherrypy.lib import httputil
def vhost_dispatch(path_info):
request = cherrypy.serving.request
header = request.headers.get
domain = header('Host', '')
if use_x_forwarded_host:
domain = header("X-Forwarded-Host", domain)
prefix = domains.get(domain, "")
if prefix:
path_info = httputil.urljoin(prefix, path_info)
result = next_dispatcher(path_info)
# Touch up staticdir config. See
# https://github.com/cherrypy/cherrypy/issues/614.
section = request.config.get('tools.staticdir.section')
if section:
section = section[len(prefix):]
request.config['tools.staticdir.section'] = section
return result
return vhost_dispatch

622
deps/cherrypy/_cperror.py vendored Normal file
View File

@@ -0,0 +1,622 @@
"""Exception classes for CherryPy.
CherryPy provides (and uses) exceptions for declaring that the HTTP response
should be a status other than the default "200 OK". You can ``raise`` them like
normal Python exceptions. You can also call them and they will raise
themselves; this means you can set an
:class:`HTTPError<cherrypy._cperror.HTTPError>`
or :class:`HTTPRedirect<cherrypy._cperror.HTTPRedirect>` as the
:attr:`request.handler<cherrypy._cprequest.Request.handler>`.
.. _redirectingpost:
Redirecting POST
================
When you GET a resource and are redirected by the server to another Location,
there's generally no problem since GET is both a "safe method" (there should
be no side-effects) and an "idempotent method" (multiple calls are no different
than a single call).
POST, however, is neither safe nor idempotent--if you
charge a credit card, you don't want to be charged twice by a redirect!
For this reason, *none* of the 3xx responses permit a user-agent (browser) to
resubmit a POST on redirection without first confirming the action with the
user:
===== ================================= ===========
300 Multiple Choices Confirm with the user
301 Moved Permanently Confirm with the user
302 Found (Object moved temporarily) Confirm with the user
303 See Other GET the new URI--no confirmation
304 Not modified (for conditional GET only--POST should not raise this error)
305 Use Proxy Confirm with the user
307 Temporary Redirect Confirm with the user
===== ================================= ===========
However, browsers have historically implemented these restrictions poorly;
in particular, many browsers do not force the user to confirm 301, 302
or 307 when redirecting POST. For this reason, CherryPy defaults to 303,
which most user-agents appear to have implemented correctly. Therefore, if
you raise HTTPRedirect for a POST request, the user-agent will most likely
attempt to GET the new URI (without asking for confirmation from the user).
We realize this is confusing for developers, but it's the safest thing we
could do. You are of course free to raise ``HTTPRedirect(uri, status=302)``
or any other 3xx status if you know what you're doing, but given the
environment, we couldn't let any of those be the default.
Custom Error Handling
=====================
.. image:: /refman/cperrors.gif
Anticipated HTTP responses
--------------------------
The 'error_page' config namespace can be used to provide custom HTML output for
expected responses (like 404 Not Found). Supply a filename from which the
output will be read. The contents will be interpolated with the values
%(status)s, %(message)s, %(traceback)s, and %(version)s using plain old Python
`string formatting <http://docs.python.org/2/library/stdtypes.html#string-formatting-operations>`_.
::
_cp_config = {
'error_page.404': os.path.join(localDir, "static/index.html")
}
Beginning in version 3.1, you may also provide a function or other callable as
an error_page entry. It will be passed the same status, message, traceback and
version arguments that are interpolated into templates::
def error_page_402(status, message, traceback, version):
return "Error %s - Well, I'm very sorry but you haven't paid!" % status
cherrypy.config.update({'error_page.402': error_page_402})
Also in 3.1, in addition to the numbered error codes, you may also supply
"error_page.default" to handle all codes which do not have their own error_page
entry.
Unanticipated errors
--------------------
CherryPy also has a generic error handling mechanism: whenever an unanticipated
error occurs in your code, it will call
:func:`Request.error_response<cherrypy._cprequest.Request.error_response>` to
set the response status, headers, and body. By default, this is the same
output as
:class:`HTTPError(500) <cherrypy._cperror.HTTPError>`. If you want to provide
some other behavior, you generally replace "request.error_response".
Here is some sample code that shows how to display a custom error message and
send an e-mail containing the error::
from cherrypy import _cperror
def handle_error():
cherrypy.response.status = 500
cherrypy.response.body = [
"<html><body>Sorry, an error occured</body></html>"
]
sendMail('error@domain.com',
'Error in your web app',
_cperror.format_exc())
@cherrypy.config(**{'request.error_response': handle_error})
class Root:
pass
Note that you have to explicitly set
:attr:`response.body <cherrypy._cprequest.Response.body>`
and not simply return an error message as a result.
"""
import contextlib
from cgi import escape as _escape
from sys import exc_info as _exc_info
from traceback import format_exception as _format_exception
from xml.sax import saxutils
import six
from cherrypy._cpcompat import text_or_bytes, iteritems, ntob
from cherrypy._cpcompat import tonative, urljoin as _urljoin
from cherrypy.lib import httputil as _httputil
class CherryPyException(Exception):
"""A base class for CherryPy exceptions."""
pass
class TimeoutError(CherryPyException):
"""Exception raised when Response.timed_out is detected."""
pass
class InternalRedirect(CherryPyException):
"""Exception raised to switch to the handler for a different URL.
This exception will redirect processing to another path within the site
(without informing the client). Provide the new path as an argument when
raising the exception. Provide any params in the querystring for the new
URL.
"""
def __init__(self, path, query_string=""):
import cherrypy
self.request = cherrypy.serving.request
self.query_string = query_string
if "?" in path:
# Separate any params included in the path
path, self.query_string = path.split("?", 1)
# Note that urljoin will "do the right thing" whether url is:
# 1. a URL relative to root (e.g. "/dummy")
# 2. a URL relative to the current path
# Note that any query string will be discarded.
path = _urljoin(self.request.path_info, path)
# Set a 'path' member attribute so that code which traps this
# error can have access to it.
self.path = path
CherryPyException.__init__(self, path, self.query_string)
class HTTPRedirect(CherryPyException):
"""Exception raised when the request should be redirected.
This exception will force a HTTP redirect to the URL or URL's you give it.
The new URL must be passed as the first argument to the Exception,
e.g., HTTPRedirect(newUrl). Multiple URLs are allowed in a list.
If a URL is absolute, it will be used as-is. If it is relative, it is
assumed to be relative to the current cherrypy.request.path_info.
If one of the provided URL is a unicode object, it will be encoded
using the default encoding or the one passed in parameter.
There are multiple types of redirect, from which you can select via the
``status`` argument. If you do not provide a ``status`` arg, it defaults to
303 (or 302 if responding with HTTP/1.0).
Examples::
raise cherrypy.HTTPRedirect("")
raise cherrypy.HTTPRedirect("/abs/path", 307)
raise cherrypy.HTTPRedirect(["path1", "path2?a=1&b=2"], 301)
See :ref:`redirectingpost` for additional caveats.
"""
status = None
"""The integer HTTP status code to emit."""
urls = None
"""The list of URL's to emit."""
encoding = 'utf-8'
"""The encoding when passed urls are not native strings"""
def __init__(self, urls, status=None, encoding=None):
import cherrypy
request = cherrypy.serving.request
if isinstance(urls, text_or_bytes):
urls = [urls]
abs_urls = []
for url in urls:
url = tonative(url, encoding or self.encoding)
# Note that urljoin will "do the right thing" whether url is:
# 1. a complete URL with host (e.g. "http://www.example.com/test")
# 2. a URL relative to root (e.g. "/dummy")
# 3. a URL relative to the current path
# Note that any query string in cherrypy.request is discarded.
url = _urljoin(cherrypy.url(), url)
abs_urls.append(url)
self.urls = abs_urls
# RFC 2616 indicates a 301 response code fits our goal; however,
# browser support for 301 is quite messy. Do 302/303 instead. See
# http://www.alanflavell.org.uk/www/post-redirect.html
if status is None:
if request.protocol >= (1, 1):
status = 303
else:
status = 302
else:
status = int(status)
if status < 300 or status > 399:
raise ValueError("status must be between 300 and 399.")
self.status = status
CherryPyException.__init__(self, abs_urls, status)
def set_response(self):
"""Modify cherrypy.response status, headers, and body to represent
self.
CherryPy uses this internally, but you can also use it to create an
HTTPRedirect object and set its output without *raising* the exception.
"""
import cherrypy
response = cherrypy.serving.response
response.status = status = self.status
if status in (300, 301, 302, 303, 307):
response.headers['Content-Type'] = "text/html;charset=utf-8"
# "The ... URI SHOULD be given by the Location field
# in the response."
response.headers['Location'] = self.urls[0]
# "Unless the request method was HEAD, the entity of the response
# SHOULD contain a short hypertext note with a hyperlink to the
# new URI(s)."
msg = {
300: "This resource can be found at ",
301: "This resource has permanently moved to ",
302: "This resource resides temporarily at ",
303: "This resource can be found at ",
307: "This resource has moved temporarily to ",
}[status]
msg += '<a href=%s>%s</a>.'
msgs = [msg % (saxutils.quoteattr(u), u) for u in self.urls]
response.body = ntob("<br />\n".join(msgs), 'utf-8')
# Previous code may have set C-L, so we have to reset it
# (allow finalize to set it).
response.headers.pop('Content-Length', None)
elif status == 304:
# Not Modified.
# "The response MUST include the following header fields:
# Date, unless its omission is required by section 14.18.1"
# The "Date" header should have been set in Response.__init__
# "...the response SHOULD NOT include other entity-headers."
for key in ('Allow', 'Content-Encoding', 'Content-Language',
'Content-Length', 'Content-Location', 'Content-MD5',
'Content-Range', 'Content-Type', 'Expires',
'Last-Modified'):
if key in response.headers:
del response.headers[key]
# "The 304 response MUST NOT contain a message-body."
response.body = None
# Previous code may have set C-L, so we have to reset it.
response.headers.pop('Content-Length', None)
elif status == 305:
# Use Proxy.
# self.urls[0] should be the URI of the proxy.
response.headers['Location'] = ntob(self.urls[0], 'utf-8')
response.body = None
# Previous code may have set C-L, so we have to reset it.
response.headers.pop('Content-Length', None)
else:
raise ValueError("The %s status code is unknown." % status)
def __call__(self):
"""Use this exception as a request.handler (raise self)."""
raise self
def clean_headers(status):
"""Remove any headers which should not apply to an error response."""
import cherrypy
response = cherrypy.serving.response
# Remove headers which applied to the original content,
# but do not apply to the error page.
respheaders = response.headers
for key in ["Accept-Ranges", "Age", "ETag", "Location", "Retry-After",
"Vary", "Content-Encoding", "Content-Length", "Expires",
"Content-Location", "Content-MD5", "Last-Modified"]:
if key in respheaders:
del respheaders[key]
if status != 416:
# A server sending a response with status code 416 (Requested
# range not satisfiable) SHOULD include a Content-Range field
# with a byte-range-resp-spec of "*". The instance-length
# specifies the current length of the selected resource.
# A response with status code 206 (Partial Content) MUST NOT
# include a Content-Range field with a byte-range- resp-spec of "*".
if "Content-Range" in respheaders:
del respheaders["Content-Range"]
class HTTPError(CherryPyException):
"""Exception used to return an HTTP error code (4xx-5xx) to the client.
This exception can be used to automatically send a response using a
http status code, with an appropriate error page. It takes an optional
``status`` argument (which must be between 400 and 599); it defaults to 500
("Internal Server Error"). It also takes an optional ``message`` argument,
which will be returned in the response body. See
`RFC2616 <http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4>`_
for a complete list of available error codes and when to use them.
Examples::
raise cherrypy.HTTPError(403)
raise cherrypy.HTTPError(
"403 Forbidden", "You are not allowed to access this resource.")
"""
status = None
"""The HTTP status code. May be of type int or str (with a Reason-Phrase).
"""
code = None
"""The integer HTTP status code."""
reason = None
"""The HTTP Reason-Phrase string."""
def __init__(self, status=500, message=None):
self.status = status
try:
self.code, self.reason, defaultmsg = _httputil.valid_status(status)
except ValueError:
raise self.__class__(500, _exc_info()[1].args[0])
if self.code < 400 or self.code > 599:
raise ValueError("status must be between 400 and 599.")
# See http://www.python.org/dev/peps/pep-0352/
# self.message = message
self._message = message or defaultmsg
CherryPyException.__init__(self, status, message)
def set_response(self):
"""Modify cherrypy.response status, headers, and body to represent
self.
CherryPy uses this internally, but you can also use it to create an
HTTPError object and set its output without *raising* the exception.
"""
import cherrypy
response = cherrypy.serving.response
clean_headers(self.code)
# In all cases, finalize will be called after this method,
# so don't bother cleaning up response values here.
response.status = self.status
tb = None
if cherrypy.serving.request.show_tracebacks:
tb = format_exc()
response.headers.pop('Content-Length', None)
content = self.get_error_page(self.status, traceback=tb,
message=self._message)
response.body = content
_be_ie_unfriendly(self.code)
def get_error_page(self, *args, **kwargs):
return get_error_page(*args, **kwargs)
def __call__(self):
"""Use this exception as a request.handler (raise self)."""
raise self
@classmethod
@contextlib.contextmanager
def handle(cls, exception, status=500, message=''):
"""Translate exception into an HTTPError."""
try:
yield
except exception as exc:
raise cls(status, message or str(exc))
class NotFound(HTTPError):
"""Exception raised when a URL could not be mapped to any handler (404).
This is equivalent to raising
:class:`HTTPError("404 Not Found") <cherrypy._cperror.HTTPError>`.
"""
def __init__(self, path=None):
if path is None:
import cherrypy
request = cherrypy.serving.request
path = request.script_name + request.path_info
self.args = (path,)
HTTPError.__init__(self, 404, "The path '%s' was not found." % path)
_HTTPErrorTemplate = '''<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
<title>%(status)s</title>
<style type="text/css">
#powered_by {
margin-top: 20px;
border-top: 2px solid black;
font-style: italic;
}
#traceback {
color: red;
}
</style>
</head>
<body>
<h2>%(status)s</h2>
<p>%(message)s</p>
<pre id="traceback">%(traceback)s</pre>
<div id="powered_by">
<span>
Powered by <a href="http://www.cherrypy.org">CherryPy %(version)s</a>
</span>
</div>
</body>
</html>
'''
def get_error_page(status, **kwargs):
"""Return an HTML page, containing a pretty error response.
status should be an int or a str.
kwargs will be interpolated into the page template.
"""
import cherrypy
try:
code, reason, message = _httputil.valid_status(status)
except ValueError:
raise cherrypy.HTTPError(500, _exc_info()[1].args[0])
# We can't use setdefault here, because some
# callers send None for kwarg values.
if kwargs.get('status') is None:
kwargs['status'] = "%s %s" % (code, reason)
if kwargs.get('message') is None:
kwargs['message'] = message
if kwargs.get('traceback') is None:
kwargs['traceback'] = ''
if kwargs.get('version') is None:
kwargs['version'] = cherrypy.__version__
for k, v in iteritems(kwargs):
if v is None:
kwargs[k] = ""
else:
kwargs[k] = _escape(kwargs[k])
# Use a custom template or callable for the error page?
pages = cherrypy.serving.request.error_page
error_page = pages.get(code) or pages.get('default')
# Default template, can be overridden below.
template = _HTTPErrorTemplate
if error_page:
try:
if hasattr(error_page, '__call__'):
# The caller function may be setting headers manually,
# so we delegate to it completely. We may be returning
# an iterator as well as a string here.
#
# We *must* make sure any content is not unicode.
result = error_page(**kwargs)
if cherrypy.lib.is_iterator(result):
from cherrypy.lib.encoding import UTF8StreamEncoder
return UTF8StreamEncoder(result)
elif isinstance(result, six.text_type):
return result.encode('utf-8')
else:
if not isinstance(result, bytes):
raise ValueError('error page function did not '
'return a bytestring, six.text_typeing or an '
'iterator - returned object of type %s.'
% (type(result).__name__))
return result
else:
# Load the template from this path.
template = tonative(open(error_page, 'rb').read())
except:
e = _format_exception(*_exc_info())[-1]
m = kwargs['message']
if m:
m += "<br />"
m += "In addition, the custom error page failed:\n<br />%s" % e
kwargs['message'] = m
response = cherrypy.serving.response
response.headers['Content-Type'] = "text/html;charset=utf-8"
result = template % kwargs
return result.encode('utf-8')
_ie_friendly_error_sizes = {
400: 512, 403: 256, 404: 512, 405: 256,
406: 512, 408: 512, 409: 512, 410: 256,
500: 512, 501: 512, 505: 512,
}
def _be_ie_unfriendly(status):
import cherrypy
response = cherrypy.serving.response
# For some statuses, Internet Explorer 5+ shows "friendly error
# messages" instead of our response.body if the body is smaller
# than a given size. Fix this by returning a body over that size
# (by adding whitespace).
# See http://support.microsoft.com/kb/q218155/
s = _ie_friendly_error_sizes.get(status, 0)
if s:
s += 1
# Since we are issuing an HTTP error status, we assume that
# the entity is short, and we should just collapse it.
content = response.collapse_body()
l = len(content)
if l and l < s:
# IN ADDITION: the response must be written to IE
# in one chunk or it will still get replaced! Bah.
content = content + (ntob(" ") * (s - l))
response.body = content
response.headers['Content-Length'] = str(len(content))
def format_exc(exc=None):
"""Return exc (or sys.exc_info if None), formatted."""
try:
if exc is None:
exc = _exc_info()
if exc == (None, None, None):
return ""
import traceback
return "".join(traceback.format_exception(*exc))
finally:
del exc
def bare_error(extrabody=None):
"""Produce status, headers, body for a critical error.
Returns a triple without calling any other questionable functions,
so it should be as error-free as possible. Call it from an HTTP server
if you get errors outside of the request.
If extrabody is None, a friendly but rather unhelpful error message
is set in the body. If extrabody is a string, it will be appended
as-is to the body.
"""
# The whole point of this function is to be a last line-of-defense
# in handling errors. That is, it must not raise any errors itself;
# it cannot be allowed to fail. Therefore, don't add to it!
# In particular, don't call any other CP functions.
body = ntob("Unrecoverable error in the server.")
if extrabody is not None:
if not isinstance(extrabody, bytes):
extrabody = extrabody.encode('utf-8')
body += ntob("\n") + extrabody
return (ntob("500 Internal Server Error"),
[(ntob('Content-Type'), ntob('text/plain')),
(ntob('Content-Length'), ntob(str(len(body)), 'ISO-8859-1'))],
[body])

462
deps/cherrypy/_cplogging.py vendored Normal file
View File

@@ -0,0 +1,462 @@
"""
Simple config
=============
Although CherryPy uses the :mod:`Python logging module <logging>`, it does so
behind the scenes so that simple logging is simple, but complicated logging
is still possible. "Simple" logging means that you can log to the screen
(i.e. console/stdout) or to a file, and that you can easily have separate
error and access log files.
Here are the simplified logging settings. You use these by adding lines to
your config file or dict. You should set these at either the global level or
per application (see next), but generally not both.
* ``log.screen``: Set this to True to have both "error" and "access" messages
printed to stdout.
* ``log.access_file``: Set this to an absolute filename where you want
"access" messages written.
* ``log.error_file``: Set this to an absolute filename where you want "error"
messages written.
Many events are automatically logged; to log your own application events, call
:func:`cherrypy.log`.
Architecture
============
Separate scopes
---------------
CherryPy provides log managers at both the global and application layers.
This means you can have one set of logging rules for your entire site,
and another set of rules specific to each application. The global log
manager is found at :func:`cherrypy.log`, and the log manager for each
application is found at :attr:`app.log<cherrypy._cptree.Application.log>`.
If you're inside a request, the latter is reachable from
``cherrypy.request.app.log``; if you're outside a request, you'll have to
obtain a reference to the ``app``: either the return value of
:func:`tree.mount()<cherrypy._cptree.Tree.mount>` or, if you used
:func:`quickstart()<cherrypy.quickstart>` instead, via
``cherrypy.tree.apps['/']``.
By default, the global logs are named "cherrypy.error" and "cherrypy.access",
and the application logs are named "cherrypy.error.2378745" and
"cherrypy.access.2378745" (the number is the id of the Application object).
This means that the application logs "bubble up" to the site logs, so if your
application has no log handlers, the site-level handlers will still log the
messages.
Errors vs. Access
-----------------
Each log manager handles both "access" messages (one per HTTP request) and
"error" messages (everything else). Note that the "error" log is not just for
errors! The format of access messages is highly formalized, but the error log
isn't--it receives messages from a variety of sources (including full error
tracebacks, if enabled).
If you are logging the access log and error log to the same source, then there
is a possibility that a specially crafted error message may replicate an access
log message as described in CWE-117. In this case it is the application
developer's responsibility to manually escape data before using CherryPy's log()
functionality, or they may create an application that is vulnerable to CWE-117.
This would be achieved by using a custom handler escape any special characters,
and attached as described below.
Custom Handlers
===============
The simple settings above work by manipulating Python's standard :mod:`logging`
module. So when you need something more complex, the full power of the standard
module is yours to exploit. You can borrow or create custom handlers, formats,
filters, and much more. Here's an example that skips the standard FileHandler
and uses a RotatingFileHandler instead:
::
#python
log = app.log
# Remove the default FileHandlers if present.
log.error_file = ""
log.access_file = ""
maxBytes = getattr(log, "rot_maxBytes", 10000000)
backupCount = getattr(log, "rot_backupCount", 1000)
# Make a new RotatingFileHandler for the error log.
fname = getattr(log, "rot_error_file", "error.log")
h = handlers.RotatingFileHandler(fname, 'a', maxBytes, backupCount)
h.setLevel(DEBUG)
h.setFormatter(_cplogging.logfmt)
log.error_log.addHandler(h)
# Make a new RotatingFileHandler for the access log.
fname = getattr(log, "rot_access_file", "access.log")
h = handlers.RotatingFileHandler(fname, 'a', maxBytes, backupCount)
h.setLevel(DEBUG)
h.setFormatter(_cplogging.logfmt)
log.access_log.addHandler(h)
The ``rot_*`` attributes are pulled straight from the application log object.
Since "log.*" config entries simply set attributes on the log object, you can
add custom attributes to your heart's content. Note that these handlers are
used ''instead'' of the default, simple handlers outlined above (so don't set
the "log.error_file" config entry, for example).
"""
import datetime
import logging
# Silence the no-handlers "warning" (stderr write!) in stdlib logging
logging.Logger.manager.emittedNoHandlerWarning = 1
logfmt = logging.Formatter("%(message)s")
import os
import sys
import six
import cherrypy
from cherrypy import _cperror
from cherrypy._cpcompat import ntob
class NullHandler(logging.Handler):
"""A no-op logging handler to silence the logging.lastResort handler."""
def handle(self, record):
pass
def emit(self, record):
pass
def createLock(self):
self.lock = None
class LogManager(object):
"""An object to assist both simple and advanced logging.
``cherrypy.log`` is an instance of this class.
"""
appid = None
"""The id() of the Application object which owns this log manager. If this
is a global log manager, appid is None."""
error_log = None
"""The actual :class:`logging.Logger` instance for error messages."""
access_log = None
"""The actual :class:`logging.Logger` instance for access messages."""
access_log_format = (
'{h} {l} {u} {t} "{r}" {s} {b} "{f}" "{a}"'
if six.PY3 else
'%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'
)
logger_root = None
"""The "top-level" logger name.
This string will be used as the first segment in the Logger names.
The default is "cherrypy", for example, in which case the Logger names
will be of the form::
cherrypy.error.<appid>
cherrypy.access.<appid>
"""
def __init__(self, appid=None, logger_root="cherrypy"):
self.logger_root = logger_root
self.appid = appid
if appid is None:
self.error_log = logging.getLogger("%s.error" % logger_root)
self.access_log = logging.getLogger("%s.access" % logger_root)
else:
self.error_log = logging.getLogger(
"%s.error.%s" % (logger_root, appid))
self.access_log = logging.getLogger(
"%s.access.%s" % (logger_root, appid))
self.error_log.setLevel(logging.INFO)
self.access_log.setLevel(logging.INFO)
# Silence the no-handlers "warning" (stderr write!) in stdlib logging
self.error_log.addHandler(NullHandler())
self.access_log.addHandler(NullHandler())
cherrypy.engine.subscribe('graceful', self.reopen_files)
def reopen_files(self):
"""Close and reopen all file handlers."""
for log in (self.error_log, self.access_log):
for h in log.handlers:
if isinstance(h, logging.FileHandler):
h.acquire()
h.stream.close()
h.stream = open(h.baseFilename, h.mode)
h.release()
def error(self, msg='', context='', severity=logging.INFO,
traceback=False):
"""Write the given ``msg`` to the error log.
This is not just for errors! Applications may call this at any time
to log application-specific information.
If ``traceback`` is True, the traceback of the current exception
(if any) will be appended to ``msg``.
"""
exc_info = None
if traceback:
exc_info = _cperror._exc_info()
self.error_log.log(severity, ' '.join((self.time(), context, msg)), exc_info=exc_info)
def __call__(self, *args, **kwargs):
"""An alias for ``error``."""
return self.error(*args, **kwargs)
def access(self):
"""Write to the access log (in Apache/NCSA Combined Log format).
See the
`apache documentation <http://httpd.apache.org/docs/current/logs.html#combined>`_
for format details.
CherryPy calls this automatically for you. Note there are no arguments;
it collects the data itself from
:class:`cherrypy.request<cherrypy._cprequest.Request>`.
Like Apache started doing in 2.0.46, non-printable and other special
characters in %r (and we expand that to all parts) are escaped using
\\xhh sequences, where hh stands for the hexadecimal representation
of the raw byte. Exceptions from this rule are " and \\, which are
escaped by prepending a backslash, and all whitespace characters,
which are written in their C-style notation (\\n, \\t, etc).
"""
request = cherrypy.serving.request
remote = request.remote
response = cherrypy.serving.response
outheaders = response.headers
inheaders = request.headers
if response.output_status is None:
status = "-"
else:
status = response.output_status.split(ntob(" "), 1)[0]
if six.PY3:
status = status.decode('ISO-8859-1')
atoms = {'h': remote.name or remote.ip,
'l': '-',
'u': getattr(request, "login", None) or "-",
't': self.time(),
'r': request.request_line,
's': status,
'b': dict.get(outheaders, 'Content-Length', '') or "-",
'f': dict.get(inheaders, 'Referer', ''),
'a': dict.get(inheaders, 'User-Agent', ''),
'o': dict.get(inheaders, 'Host', '-'),
}
if six.PY3:
for k, v in atoms.items():
if not isinstance(v, str):
v = str(v)
v = v.replace('"', '\\"').encode('utf8')
# Fortunately, repr(str) escapes unprintable chars, \n, \t, etc
# and backslash for us. All we have to do is strip the quotes.
v = repr(v)[2:-1]
# in python 3.0 the repr of bytes (as returned by encode)
# uses double \'s. But then the logger escapes them yet, again
# resulting in quadruple slashes. Remove the extra one here.
v = v.replace('\\\\', '\\')
# Escape double-quote.
atoms[k] = v
try:
self.access_log.log(
logging.INFO, self.access_log_format.format(**atoms))
except:
self(traceback=True)
else:
for k, v in atoms.items():
if isinstance(v, six.text_type):
v = v.encode('utf8')
elif not isinstance(v, str):
v = str(v)
# Fortunately, repr(str) escapes unprintable chars, \n, \t, etc
# and backslash for us. All we have to do is strip the quotes.
v = repr(v)[1:-1]
# Escape double-quote.
atoms[k] = v.replace('"', '\\"')
try:
self.access_log.log(
logging.INFO, self.access_log_format % atoms)
except:
self(traceback=True)
def time(self):
"""Return now() in Apache Common Log Format (no timezone)."""
now = datetime.datetime.now()
monthnames = ['jan', 'feb', 'mar', 'apr', 'may', 'jun',
'jul', 'aug', 'sep', 'oct', 'nov', 'dec']
month = monthnames[now.month - 1].capitalize()
return ('[%02d/%s/%04d:%02d:%02d:%02d]' %
(now.day, month, now.year, now.hour, now.minute, now.second))
def _get_builtin_handler(self, log, key):
for h in log.handlers:
if getattr(h, "_cpbuiltin", None) == key:
return h
# ------------------------- Screen handlers ------------------------- #
def _set_screen_handler(self, log, enable, stream=None):
h = self._get_builtin_handler(log, "screen")
if enable:
if not h:
if stream is None:
stream = sys.stderr
h = logging.StreamHandler(stream)
h.setFormatter(logfmt)
h._cpbuiltin = "screen"
log.addHandler(h)
elif h:
log.handlers.remove(h)
def _get_screen(self):
h = self._get_builtin_handler
has_h = h(self.error_log, "screen") or h(self.access_log, "screen")
return bool(has_h)
def _set_screen(self, newvalue):
self._set_screen_handler(self.error_log, newvalue, stream=sys.stderr)
self._set_screen_handler(self.access_log, newvalue, stream=sys.stdout)
screen = property(_get_screen, _set_screen,
doc="""Turn stderr/stdout logging on or off.
If you set this to True, it'll add the appropriate StreamHandler for
you. If you set it to False, it will remove the handler.
""")
# -------------------------- File handlers -------------------------- #
def _add_builtin_file_handler(self, log, fname):
h = logging.FileHandler(fname)
h.setFormatter(logfmt)
h._cpbuiltin = "file"
log.addHandler(h)
def _set_file_handler(self, log, filename):
h = self._get_builtin_handler(log, "file")
if filename:
if h:
if h.baseFilename != os.path.abspath(filename):
h.close()
log.handlers.remove(h)
self._add_builtin_file_handler(log, filename)
else:
self._add_builtin_file_handler(log, filename)
else:
if h:
h.close()
log.handlers.remove(h)
def _get_error_file(self):
h = self._get_builtin_handler(self.error_log, "file")
if h:
return h.baseFilename
return ''
def _set_error_file(self, newvalue):
self._set_file_handler(self.error_log, newvalue)
error_file = property(_get_error_file, _set_error_file,
doc="""The filename for self.error_log.
If you set this to a string, it'll add the appropriate FileHandler for
you. If you set it to ``None`` or ``''``, it will remove the handler.
""")
def _get_access_file(self):
h = self._get_builtin_handler(self.access_log, "file")
if h:
return h.baseFilename
return ''
def _set_access_file(self, newvalue):
self._set_file_handler(self.access_log, newvalue)
access_file = property(_get_access_file, _set_access_file,
doc="""The filename for self.access_log.
If you set this to a string, it'll add the appropriate FileHandler for
you. If you set it to ``None`` or ``''``, it will remove the handler.
""")
# ------------------------- WSGI handlers ------------------------- #
def _set_wsgi_handler(self, log, enable):
h = self._get_builtin_handler(log, "wsgi")
if enable:
if not h:
h = WSGIErrorHandler()
h.setFormatter(logfmt)
h._cpbuiltin = "wsgi"
log.addHandler(h)
elif h:
log.handlers.remove(h)
def _get_wsgi(self):
return bool(self._get_builtin_handler(self.error_log, "wsgi"))
def _set_wsgi(self, newvalue):
self._set_wsgi_handler(self.error_log, newvalue)
wsgi = property(_get_wsgi, _set_wsgi,
doc="""Write errors to wsgi.errors.
If you set this to True, it'll add the appropriate
:class:`WSGIErrorHandler<cherrypy._cplogging.WSGIErrorHandler>` for you
(which writes errors to ``wsgi.errors``).
If you set it to False, it will remove the handler.
""")
class WSGIErrorHandler(logging.Handler):
"A handler class which writes logging records to environ['wsgi.errors']."
def flush(self):
"""Flushes the stream."""
try:
stream = cherrypy.serving.request.wsgi_environ.get('wsgi.errors')
except (AttributeError, KeyError):
pass
else:
stream.flush()
def emit(self, record):
"""Emit a record."""
try:
stream = cherrypy.serving.request.wsgi_environ.get('wsgi.errors')
except (AttributeError, KeyError):
pass
else:
try:
msg = self.format(record)
fs = "%s\n"
import types
# if no unicode support...
if not hasattr(types, "UnicodeType"):
stream.write(fs % msg)
else:
try:
stream.write(fs % msg)
except UnicodeError:
stream.write(fs % msg.encode("UTF-8"))
self.flush()
except:
self.handleError(record)

354
deps/cherrypy/_cpmodpy.py vendored Normal file
View File

@@ -0,0 +1,354 @@
"""Native adapter for serving CherryPy via mod_python
Basic usage:
##########################################
# Application in a module called myapp.py
##########################################
import cherrypy
class Root:
@cherrypy.expose
def index(self):
return 'Hi there, Ho there, Hey there'
# We will use this method from the mod_python configuration
# as the entry point to our application
def setup_server():
cherrypy.tree.mount(Root())
cherrypy.config.update({'environment': 'production',
'log.screen': False,
'show_tracebacks': False})
##########################################
# mod_python settings for apache2
# This should reside in your httpd.conf
# or a file that will be loaded at
# apache startup
##########################################
# Start
DocumentRoot "/"
Listen 8080
LoadModule python_module /usr/lib/apache2/modules/mod_python.so
<Location "/">
PythonPath "sys.path+['/path/to/my/application']"
SetHandler python-program
PythonHandler cherrypy._cpmodpy::handler
PythonOption cherrypy.setup myapp::setup_server
PythonDebug On
</Location>
# End
The actual path to your mod_python.so is dependent on your
environment. In this case we suppose a global mod_python
installation on a Linux distribution such as Ubuntu.
We do set the PythonPath configuration setting so that
your application can be found by from the user running
the apache2 instance. Of course if your application
resides in the global site-package this won't be needed.
Then restart apache2 and access http://127.0.0.1:8080
"""
import logging
import sys
import io
import cherrypy
from cherrypy._cpcompat import copyitems, ntob
from cherrypy._cperror import format_exc, bare_error
from cherrypy.lib import httputil
# ------------------------------ Request-handling
def setup(req):
from mod_python import apache
# Run any setup functions defined by a "PythonOption cherrypy.setup"
# directive.
options = req.get_options()
if 'cherrypy.setup' in options:
for function in options['cherrypy.setup'].split():
atoms = function.split('::', 1)
if len(atoms) == 1:
mod = __import__(atoms[0], globals(), locals())
else:
modname, fname = atoms
mod = __import__(modname, globals(), locals(), [fname])
func = getattr(mod, fname)
func()
cherrypy.config.update({'log.screen': False,
"tools.ignore_headers.on": True,
"tools.ignore_headers.headers": ['Range'],
})
engine = cherrypy.engine
if hasattr(engine, "signal_handler"):
engine.signal_handler.unsubscribe()
if hasattr(engine, "console_control_handler"):
engine.console_control_handler.unsubscribe()
engine.autoreload.unsubscribe()
cherrypy.server.unsubscribe()
def _log(msg, level):
newlevel = apache.APLOG_ERR
if logging.DEBUG >= level:
newlevel = apache.APLOG_DEBUG
elif logging.INFO >= level:
newlevel = apache.APLOG_INFO
elif logging.WARNING >= level:
newlevel = apache.APLOG_WARNING
# On Windows, req.server is required or the msg will vanish. See
# http://www.modpython.org/pipermail/mod_python/2003-October/014291.html
# Also, "When server is not specified...LogLevel does not apply..."
apache.log_error(msg, newlevel, req.server)
engine.subscribe('log', _log)
engine.start()
def cherrypy_cleanup(data):
engine.exit()
try:
# apache.register_cleanup wasn't available until 3.1.4.
apache.register_cleanup(cherrypy_cleanup)
except AttributeError:
req.server.register_cleanup(req, cherrypy_cleanup)
class _ReadOnlyRequest:
expose = ('read', 'readline', 'readlines')
def __init__(self, req):
for method in self.expose:
self.__dict__[method] = getattr(req, method)
recursive = False
_isSetUp = False
def handler(req):
from mod_python import apache
try:
global _isSetUp
if not _isSetUp:
setup(req)
_isSetUp = True
# Obtain a Request object from CherryPy
local = req.connection.local_addr
local = httputil.Host(
local[0], local[1], req.connection.local_host or "")
remote = req.connection.remote_addr
remote = httputil.Host(
remote[0], remote[1], req.connection.remote_host or "")
scheme = req.parsed_uri[0] or 'http'
req.get_basic_auth_pw()
try:
# apache.mpm_query only became available in mod_python 3.1
q = apache.mpm_query
threaded = q(apache.AP_MPMQ_IS_THREADED)
forked = q(apache.AP_MPMQ_IS_FORKED)
except AttributeError:
bad_value = ("You must provide a PythonOption '%s', "
"either 'on' or 'off', when running a version "
"of mod_python < 3.1")
threaded = options.get('multithread', '').lower()
if threaded == 'on':
threaded = True
elif threaded == 'off':
threaded = False
else:
raise ValueError(bad_value % "multithread")
forked = options.get('multiprocess', '').lower()
if forked == 'on':
forked = True
elif forked == 'off':
forked = False
else:
raise ValueError(bad_value % "multiprocess")
sn = cherrypy.tree.script_name(req.uri or "/")
if sn is None:
send_response(req, '404 Not Found', [], '')
else:
app = cherrypy.tree.apps[sn]
method = req.method
path = req.uri
qs = req.args or ""
reqproto = req.protocol
headers = copyitems(req.headers_in)
rfile = _ReadOnlyRequest(req)
prev = None
try:
redirections = []
while True:
request, response = app.get_serving(local, remote, scheme,
"HTTP/1.1")
request.login = req.user
request.multithread = bool(threaded)
request.multiprocess = bool(forked)
request.app = app
request.prev = prev
# Run the CherryPy Request object and obtain the response
try:
request.run(method, path, qs, reqproto, headers, rfile)
break
except cherrypy.InternalRedirect:
ir = sys.exc_info()[1]
app.release_serving()
prev = request
if not recursive:
if ir.path in redirections:
raise RuntimeError(
"InternalRedirector visited the same URL "
"twice: %r" % ir.path)
else:
# Add the *previous* path_info + qs to
# redirections.
if qs:
qs = "?" + qs
redirections.append(sn + path + qs)
# Munge environment and try again.
method = "GET"
path = ir.path
qs = ir.query_string
rfile = io.BytesIO()
send_response(
req, response.output_status, response.header_list,
response.body, response.stream)
finally:
app.release_serving()
except:
tb = format_exc()
cherrypy.log(tb, 'MOD_PYTHON', severity=logging.ERROR)
s, h, b = bare_error()
send_response(req, s, h, b)
return apache.OK
def send_response(req, status, headers, body, stream=False):
# Set response status
req.status = int(status[:3])
# Set response headers
req.content_type = "text/plain"
for header, value in headers:
if header.lower() == 'content-type':
req.content_type = value
continue
req.headers_out.add(header, value)
if stream:
# Flush now so the status and headers are sent immediately.
req.flush()
# Set response body
if isinstance(body, text_or_bytes):
req.write(body)
else:
for seg in body:
req.write(seg)
# --------------- Startup tools for CherryPy + mod_python --------------- #
import os
import re
try:
import subprocess
def popen(fullcmd):
p = subprocess.Popen(fullcmd, shell=True,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
close_fds=True)
return p.stdout
except ImportError:
def popen(fullcmd):
pipein, pipeout = os.popen4(fullcmd)
return pipeout
def read_process(cmd, args=""):
fullcmd = "%s %s" % (cmd, args)
pipeout = popen(fullcmd)
try:
firstline = pipeout.readline()
cmd_not_found = re.search(
ntob("(not recognized|No such file|not found)"),
firstline,
re.IGNORECASE
)
if cmd_not_found:
raise IOError('%s must be on your system path.' % cmd)
output = firstline + pipeout.read()
finally:
pipeout.close()
return output
class ModPythonServer(object):
template = """
# Apache2 server configuration file for running CherryPy with mod_python.
DocumentRoot "/"
Listen %(port)s
LoadModule python_module modules/mod_python.so
<Location %(loc)s>
SetHandler python-program
PythonHandler %(handler)s
PythonDebug On
%(opts)s
</Location>
"""
def __init__(self, loc="/", port=80, opts=None, apache_path="apache",
handler="cherrypy._cpmodpy::handler"):
self.loc = loc
self.port = port
self.opts = opts
self.apache_path = apache_path
self.handler = handler
def start(self):
opts = "".join([" PythonOption %s %s\n" % (k, v)
for k, v in self.opts])
conf_data = self.template % {"port": self.port,
"loc": self.loc,
"opts": opts,
"handler": self.handler,
}
mpconf = os.path.join(os.path.dirname(__file__), "cpmodpy.conf")
f = open(mpconf, 'wb')
try:
f.write(conf_data)
finally:
f.close()
response = read_process(self.apache_path, "-k start -f %s" % mpconf)
self.ready = True
return response
def stop(self):
os.popen("apache -k stop")
self.ready = False

154
deps/cherrypy/_cpnative_server.py vendored Normal file
View File

@@ -0,0 +1,154 @@
"""Native adapter for serving CherryPy via its builtin server."""
import logging
import sys
import io
import cherrypy
from cherrypy._cperror import format_exc, bare_error
from cherrypy.lib import httputil
from cherrypy import wsgiserver
class NativeGateway(wsgiserver.Gateway):
recursive = False
def respond(self):
req = self.req
try:
# Obtain a Request object from CherryPy
local = req.server.bind_addr
local = httputil.Host(local[0], local[1], "")
remote = req.conn.remote_addr, req.conn.remote_port
remote = httputil.Host(remote[0], remote[1], "")
scheme = req.scheme
sn = cherrypy.tree.script_name(req.uri or "/")
if sn is None:
self.send_response('404 Not Found', [], [''])
else:
app = cherrypy.tree.apps[sn]
method = req.method
path = req.path
qs = req.qs or ""
headers = req.inheaders.items()
rfile = req.rfile
prev = None
try:
redirections = []
while True:
request, response = app.get_serving(
local, remote, scheme, "HTTP/1.1")
request.multithread = True
request.multiprocess = False
request.app = app
request.prev = prev
# Run the CherryPy Request object and obtain the
# response
try:
request.run(method, path, qs,
req.request_protocol, headers, rfile)
break
except cherrypy.InternalRedirect:
ir = sys.exc_info()[1]
app.release_serving()
prev = request
if not self.recursive:
if ir.path in redirections:
raise RuntimeError(
"InternalRedirector visited the same "
"URL twice: %r" % ir.path)
else:
# Add the *previous* path_info + qs to
# redirections.
if qs:
qs = "?" + qs
redirections.append(sn + path + qs)
# Munge environment and try again.
method = "GET"
path = ir.path
qs = ir.query_string
rfile = io.BytesIO()
self.send_response(
response.output_status, response.header_list,
response.body)
finally:
app.release_serving()
except:
tb = format_exc()
# print tb
cherrypy.log(tb, 'NATIVE_ADAPTER', severity=logging.ERROR)
s, h, b = bare_error()
self.send_response(s, h, b)
def send_response(self, status, headers, body):
req = self.req
# Set response status
req.status = str(status or "500 Server Error")
# Set response headers
for header, value in headers:
req.outheaders.append((header, value))
if (req.ready and not req.sent_headers):
req.sent_headers = True
req.send_headers()
# Set response body
for seg in body:
req.write(seg)
class CPHTTPServer(wsgiserver.HTTPServer):
"""Wrapper for wsgiserver.HTTPServer.
wsgiserver has been designed to not reference CherryPy in any way,
so that it can be used in other frameworks and applications.
Therefore, we wrap it here, so we can apply some attributes
from config -> cherrypy.server -> HTTPServer.
"""
def __init__(self, server_adapter=cherrypy.server):
self.server_adapter = server_adapter
server_name = (self.server_adapter.socket_host or
self.server_adapter.socket_file or
None)
wsgiserver.HTTPServer.__init__(
self, server_adapter.bind_addr, NativeGateway,
minthreads=server_adapter.thread_pool,
maxthreads=server_adapter.thread_pool_max,
server_name=server_name)
self.max_request_header_size = (
self.server_adapter.max_request_header_size or 0)
self.max_request_body_size = (
self.server_adapter.max_request_body_size or 0)
self.request_queue_size = self.server_adapter.socket_queue_size
self.timeout = self.server_adapter.socket_timeout
self.shutdown_timeout = self.server_adapter.shutdown_timeout
self.protocol = self.server_adapter.protocol_version
self.nodelay = self.server_adapter.nodelay
ssl_module = self.server_adapter.ssl_module or 'pyopenssl'
if self.server_adapter.ssl_context:
adapter_class = wsgiserver.get_ssl_adapter_class(ssl_module)
self.ssl_adapter = adapter_class(
self.server_adapter.ssl_certificate,
self.server_adapter.ssl_private_key,
self.server_adapter.ssl_certificate_chain)
self.ssl_adapter.context = self.server_adapter.ssl_context
elif self.server_adapter.ssl_certificate:
adapter_class = wsgiserver.get_ssl_adapter_class(ssl_module)
self.ssl_adapter = adapter_class(
self.server_adapter.ssl_certificate,
self.server_adapter.ssl_private_key,
self.server_adapter.ssl_certificate_chain)

1018
deps/cherrypy/_cpreqbody.py vendored Normal file

File diff suppressed because it is too large Load Diff

973
deps/cherrypy/_cprequest.py vendored Normal file
View File

@@ -0,0 +1,973 @@
import sys
import time
import warnings
import six
import cherrypy
from cherrypy._cpcompat import text_or_bytes, copykeys, ntob
from cherrypy._cpcompat import SimpleCookie, CookieError
from cherrypy import _cpreqbody, _cpconfig
from cherrypy._cperror import format_exc, bare_error
from cherrypy.lib import httputil, file_generator
class Hook(object):
"""A callback and its metadata: failsafe, priority, and kwargs."""
callback = None
"""
The bare callable that this Hook object is wrapping, which will
be called when the Hook is called."""
failsafe = False
"""
If True, the callback is guaranteed to run even if other callbacks
from the same call point raise exceptions."""
priority = 50
"""
Defines the order of execution for a list of Hooks. Priority numbers
should be limited to the closed interval [0, 100], but values outside
this range are acceptable, as are fractional values."""
kwargs = {}
"""
A set of keyword arguments that will be passed to the
callable on each call."""
def __init__(self, callback, failsafe=None, priority=None, **kwargs):
self.callback = callback
if failsafe is None:
failsafe = getattr(callback, "failsafe", False)
self.failsafe = failsafe
if priority is None:
priority = getattr(callback, "priority", 50)
self.priority = priority
self.kwargs = kwargs
def __lt__(self, other):
# Python 3
return self.priority < other.priority
def __cmp__(self, other):
# Python 2
return cmp(self.priority, other.priority)
def __call__(self):
"""Run self.callback(**self.kwargs)."""
return self.callback(**self.kwargs)
def __repr__(self):
cls = self.__class__
return ("%s.%s(callback=%r, failsafe=%r, priority=%r, %s)"
% (cls.__module__, cls.__name__, self.callback,
self.failsafe, self.priority,
", ".join(['%s=%r' % (k, v)
for k, v in self.kwargs.items()])))
class HookMap(dict):
"""A map of call points to lists of callbacks (Hook objects)."""
def __new__(cls, points=None):
d = dict.__new__(cls)
for p in points or []:
d[p] = []
return d
def __init__(self, *a, **kw):
pass
def attach(self, point, callback, failsafe=None, priority=None, **kwargs):
"""Append a new Hook made from the supplied arguments."""
self[point].append(Hook(callback, failsafe, priority, **kwargs))
def run(self, point):
"""Execute all registered Hooks (callbacks) for the given point."""
exc = None
hooks = self[point]
hooks.sort()
for hook in hooks:
# Some hooks are guaranteed to run even if others at
# the same hookpoint fail. We will still log the failure,
# but proceed on to the next hook. The only way
# to stop all processing from one of these hooks is
# to raise SystemExit and stop the whole server.
if exc is None or hook.failsafe:
try:
hook()
except (KeyboardInterrupt, SystemExit):
raise
except (cherrypy.HTTPError, cherrypy.HTTPRedirect,
cherrypy.InternalRedirect):
exc = sys.exc_info()[1]
except:
exc = sys.exc_info()[1]
cherrypy.log(traceback=True, severity=40)
if exc:
raise exc
def __copy__(self):
newmap = self.__class__()
# We can't just use 'update' because we want copies of the
# mutable values (each is a list) as well.
for k, v in self.items():
newmap[k] = v[:]
return newmap
copy = __copy__
def __repr__(self):
cls = self.__class__
return "%s.%s(points=%r)" % (
cls.__module__,
cls.__name__,
copykeys(self)
)
# Config namespace handlers
def hooks_namespace(k, v):
"""Attach bare hooks declared in config."""
# Use split again to allow multiple hooks for a single
# hookpoint per path (e.g. "hooks.before_handler.1").
# Little-known fact you only get from reading source ;)
hookpoint = k.split(".", 1)[0]
if isinstance(v, text_or_bytes):
v = cherrypy.lib.attributes(v)
if not isinstance(v, Hook):
v = Hook(v)
cherrypy.serving.request.hooks[hookpoint].append(v)
def request_namespace(k, v):
"""Attach request attributes declared in config."""
# Provides config entries to set request.body attrs (like
# attempt_charsets).
if k[:5] == 'body.':
setattr(cherrypy.serving.request.body, k[5:], v)
else:
setattr(cherrypy.serving.request, k, v)
def response_namespace(k, v):
"""Attach response attributes declared in config."""
# Provides config entries to set default response headers
# http://cherrypy.org/ticket/889
if k[:8] == 'headers.':
cherrypy.serving.response.headers[k.split('.', 1)[1]] = v
else:
setattr(cherrypy.serving.response, k, v)
def error_page_namespace(k, v):
"""Attach error pages declared in config."""
if k != 'default':
k = int(k)
cherrypy.serving.request.error_page[k] = v
hookpoints = ['on_start_resource', 'before_request_body',
'before_handler', 'before_finalize',
'on_end_resource', 'on_end_request',
'before_error_response', 'after_error_response']
class Request(object):
"""An HTTP request.
This object represents the metadata of an HTTP request message;
that is, it contains attributes which describe the environment
in which the request URL, headers, and body were sent (if you
want tools to interpret the headers and body, those are elsewhere,
mostly in Tools). This 'metadata' consists of socket data,
transport characteristics, and the Request-Line. This object
also contains data regarding the configuration in effect for
the given URL, and the execution plan for generating a response.
"""
prev = None
"""
The previous Request object (if any). This should be None
unless we are processing an InternalRedirect."""
# Conversation/connection attributes
local = httputil.Host("127.0.0.1", 80)
"An httputil.Host(ip, port, hostname) object for the server socket."
remote = httputil.Host("127.0.0.1", 1111)
"An httputil.Host(ip, port, hostname) object for the client socket."
scheme = "http"
"""
The protocol used between client and server. In most cases,
this will be either 'http' or 'https'."""
server_protocol = "HTTP/1.1"
"""
The HTTP version for which the HTTP server is at least
conditionally compliant."""
base = ""
"""The (scheme://host) portion of the requested URL.
In some cases (e.g. when proxying via mod_rewrite), this may contain
path segments which cherrypy.url uses when constructing url's, but
which otherwise are ignored by CherryPy. Regardless, this value
MUST NOT end in a slash."""
# Request-Line attributes
request_line = ""
"""
The complete Request-Line received from the client. This is a
single string consisting of the request method, URI, and protocol
version (joined by spaces). Any final CRLF is removed."""
method = "GET"
"""
Indicates the HTTP method to be performed on the resource identified
by the Request-URI. Common methods include GET, HEAD, POST, PUT, and
DELETE. CherryPy allows any extension method; however, various HTTP
servers and gateways may restrict the set of allowable methods.
CherryPy applications SHOULD restrict the set (on a per-URI basis)."""
query_string = ""
"""
The query component of the Request-URI, a string of information to be
interpreted by the resource. The query portion of a URI follows the
path component, and is separated by a '?'. For example, the URI
'http://www.cherrypy.org/wiki?a=3&b=4' has the query component,
'a=3&b=4'."""
query_string_encoding = 'utf8'
"""
The encoding expected for query string arguments after % HEX HEX decoding).
If a query string is provided that cannot be decoded with this encoding,
404 is raised (since technically it's a different URI). If you want
arbitrary encodings to not error, set this to 'Latin-1'; you can then
encode back to bytes and re-decode to whatever encoding you like later.
"""
protocol = (1, 1)
"""The HTTP protocol version corresponding to the set
of features which should be allowed in the response. If BOTH
the client's request message AND the server's level of HTTP
compliance is HTTP/1.1, this attribute will be the tuple (1, 1).
If either is 1.0, this attribute will be the tuple (1, 0).
Lower HTTP protocol versions are not explicitly supported."""
params = {}
"""
A dict which combines query string (GET) and request entity (POST)
variables. This is populated in two stages: GET params are added
before the 'on_start_resource' hook, and POST params are added
between the 'before_request_body' and 'before_handler' hooks."""
# Message attributes
header_list = []
"""
A list of the HTTP request headers as (name, value) tuples.
In general, you should use request.headers (a dict) instead."""
headers = httputil.HeaderMap()
"""
A dict-like object containing the request headers. Keys are header
names (in Title-Case format); however, you may get and set them in
a case-insensitive manner. That is, headers['Content-Type'] and
headers['content-type'] refer to the same value. Values are header
values (decoded according to :rfc:`2047` if necessary). See also:
httputil.HeaderMap, httputil.HeaderElement."""
cookie = SimpleCookie()
"""See help(Cookie)."""
rfile = None
"""
If the request included an entity (body), it will be available
as a stream in this attribute. However, the rfile will normally
be read for you between the 'before_request_body' hook and the
'before_handler' hook, and the resulting string is placed into
either request.params or the request.body attribute.
You may disable the automatic consumption of the rfile by setting
request.process_request_body to False, either in config for the desired
path, or in an 'on_start_resource' or 'before_request_body' hook.
WARNING: In almost every case, you should not attempt to read from the
rfile stream after CherryPy's automatic mechanism has read it. If you
turn off the automatic parsing of rfile, you should read exactly the
number of bytes specified in request.headers['Content-Length'].
Ignoring either of these warnings may result in a hung request thread
or in corruption of the next (pipelined) request.
"""
process_request_body = True
"""
If True, the rfile (if any) is automatically read and parsed,
and the result placed into request.params or request.body."""
methods_with_bodies = ("POST", "PUT")
"""
A sequence of HTTP methods for which CherryPy will automatically
attempt to read a body from the rfile. If you are going to change
this property, modify it on the configuration (recommended)
or on the "hook point" `on_start_resource`.
"""
body = None
"""
If the request Content-Type is 'application/x-www-form-urlencoded'
or multipart, this will be None. Otherwise, this will be an instance
of :class:`RequestBody<cherrypy._cpreqbody.RequestBody>` (which you
can .read()); this value is set between the 'before_request_body' and
'before_handler' hooks (assuming that process_request_body is True)."""
# Dispatch attributes
dispatch = cherrypy.dispatch.Dispatcher()
"""
The object which looks up the 'page handler' callable and collects
config for the current request based on the path_info, other
request attributes, and the application architecture. The core
calls the dispatcher as early as possible, passing it a 'path_info'
argument.
The default dispatcher discovers the page handler by matching path_info
to a hierarchical arrangement of objects, starting at request.app.root.
See help(cherrypy.dispatch) for more information."""
script_name = ""
"""
The 'mount point' of the application which is handling this request.
This attribute MUST NOT end in a slash. If the script_name refers to
the root of the URI, it MUST be an empty string (not "/").
"""
path_info = "/"
"""
The 'relative path' portion of the Request-URI. This is relative
to the script_name ('mount point') of the application which is
handling this request."""
login = None
"""
When authentication is used during the request processing this is
set to 'False' if it failed and to the 'username' value if it succeeded.
The default 'None' implies that no authentication happened."""
# Note that cherrypy.url uses "if request.app:" to determine whether
# the call is during a real HTTP request or not. So leave this None.
app = None
"""The cherrypy.Application object which is handling this request."""
handler = None
"""
The function, method, or other callable which CherryPy will call to
produce the response. The discovery of the handler and the arguments
it will receive are determined by the request.dispatch object.
By default, the handler is discovered by walking a tree of objects
starting at request.app.root, and is then passed all HTTP params
(from the query string and POST body) as keyword arguments."""
toolmaps = {}
"""
A nested dict of all Toolboxes and Tools in effect for this request,
of the form: {Toolbox.namespace: {Tool.name: config dict}}."""
config = None
"""
A flat dict of all configuration entries which apply to the
current request. These entries are collected from global config,
application config (based on request.path_info), and from handler
config (exactly how is governed by the request.dispatch object in
effect for this request; by default, handler config can be attached
anywhere in the tree between request.app.root and the final handler,
and inherits downward)."""
is_index = None
"""
This will be True if the current request is mapped to an 'index'
resource handler (also, a 'default' handler if path_info ends with
a slash). The value may be used to automatically redirect the
user-agent to a 'more canonical' URL which either adds or removes
the trailing slash. See cherrypy.tools.trailing_slash."""
hooks = HookMap(hookpoints)
"""
A HookMap (dict-like object) of the form: {hookpoint: [hook, ...]}.
Each key is a str naming the hook point, and each value is a list
of hooks which will be called at that hook point during this request.
The list of hooks is generally populated as early as possible (mostly
from Tools specified in config), but may be extended at any time.
See also: _cprequest.Hook, _cprequest.HookMap, and cherrypy.tools."""
error_response = cherrypy.HTTPError(500).set_response
"""
The no-arg callable which will handle unexpected, untrapped errors
during request processing. This is not used for expected exceptions
(like NotFound, HTTPError, or HTTPRedirect) which are raised in
response to expected conditions (those should be customized either
via request.error_page or by overriding HTTPError.set_response).
By default, error_response uses HTTPError(500) to return a generic
error response to the user-agent."""
error_page = {}
"""
A dict of {error code: response filename or callable} pairs.
The error code must be an int representing a given HTTP error code,
or the string 'default', which will be used if no matching entry
is found for a given numeric code.
If a filename is provided, the file should contain a Python string-
formatting template, and can expect by default to receive format
values with the mapping keys %(status)s, %(message)s, %(traceback)s,
and %(version)s. The set of format mappings can be extended by
overriding HTTPError.set_response.
If a callable is provided, it will be called by default with keyword
arguments 'status', 'message', 'traceback', and 'version', as for a
string-formatting template. The callable must return a string or
iterable of strings which will be set to response.body. It may also
override headers or perform any other processing.
If no entry is given for an error code, and no 'default' entry exists,
a default template will be used.
"""
show_tracebacks = True
"""
If True, unexpected errors encountered during request processing will
include a traceback in the response body."""
show_mismatched_params = True
"""
If True, mismatched parameters encountered during PageHandler invocation
processing will be included in the response body."""
throws = (KeyboardInterrupt, SystemExit, cherrypy.InternalRedirect)
"""The sequence of exceptions which Request.run does not trap."""
throw_errors = False
"""
If True, Request.run will not trap any errors (except HTTPRedirect and
HTTPError, which are more properly called 'exceptions', not errors)."""
closed = False
"""True once the close method has been called, False otherwise."""
stage = None
"""
A string containing the stage reached in the request-handling process.
This is useful when debugging a live server with hung requests."""
namespaces = _cpconfig.NamespaceSet(
**{"hooks": hooks_namespace,
"request": request_namespace,
"response": response_namespace,
"error_page": error_page_namespace,
"tools": cherrypy.tools,
})
def __init__(self, local_host, remote_host, scheme="http",
server_protocol="HTTP/1.1"):
"""Populate a new Request object.
local_host should be an httputil.Host object with the server info.
remote_host should be an httputil.Host object with the client info.
scheme should be a string, either "http" or "https".
"""
self.local = local_host
self.remote = remote_host
self.scheme = scheme
self.server_protocol = server_protocol
self.closed = False
# Put a *copy* of the class error_page into self.
self.error_page = self.error_page.copy()
# Put a *copy* of the class namespaces into self.
self.namespaces = self.namespaces.copy()
self.stage = None
def close(self):
"""Run cleanup code. (Core)"""
if not self.closed:
self.closed = True
self.stage = 'on_end_request'
self.hooks.run('on_end_request')
self.stage = 'close'
def run(self, method, path, query_string, req_protocol, headers, rfile):
r"""Process the Request. (Core)
method, path, query_string, and req_protocol should be pulled directly
from the Request-Line (e.g. "GET /path?key=val HTTP/1.0").
path
This should be %XX-unquoted, but query_string should not be.
When using Python 2, they both MUST be byte strings,
not unicode strings.
When using Python 3, they both MUST be unicode strings,
not byte strings, and preferably not bytes \x00-\xFF
disguised as unicode.
headers
A list of (name, value) tuples.
rfile
A file-like object containing the HTTP request entity.
When run() is done, the returned object should have 3 attributes:
* status, e.g. "200 OK"
* header_list, a list of (name, value) tuples
* body, an iterable yielding strings
Consumer code (HTTP servers) should then access these response
attributes to build the outbound stream.
"""
response = cherrypy.serving.response
self.stage = 'run'
try:
self.error_response = cherrypy.HTTPError(500).set_response
self.method = method
path = path or "/"
self.query_string = query_string or ''
self.params = {}
# Compare request and server HTTP protocol versions, in case our
# server does not support the requested protocol. Limit our output
# to min(req, server). We want the following output:
# request server actual written supported response
# protocol protocol response protocol feature set
# a 1.0 1.0 1.0 1.0
# b 1.0 1.1 1.1 1.0
# c 1.1 1.0 1.0 1.0
# d 1.1 1.1 1.1 1.1
# Notice that, in (b), the response will be "HTTP/1.1" even though
# the client only understands 1.0. RFC 2616 10.5.6 says we should
# only return 505 if the _major_ version is different.
rp = int(req_protocol[5]), int(req_protocol[7])
sp = int(self.server_protocol[5]), int(self.server_protocol[7])
self.protocol = min(rp, sp)
response.headers.protocol = self.protocol
# Rebuild first line of the request (e.g. "GET /path HTTP/1.0").
url = path
if query_string:
url += '?' + query_string
self.request_line = '%s %s %s' % (method, url, req_protocol)
self.header_list = list(headers)
self.headers = httputil.HeaderMap()
self.rfile = rfile
self.body = None
self.cookie = SimpleCookie()
self.handler = None
# path_info should be the path from the
# app root (script_name) to the handler.
self.script_name = self.app.script_name
self.path_info = pi = path[len(self.script_name):]
self.stage = 'respond'
self.respond(pi)
except self.throws:
raise
except:
if self.throw_errors:
raise
else:
# Failure in setup, error handler or finalize. Bypass them.
# Can't use handle_error because we may not have hooks yet.
cherrypy.log(traceback=True, severity=40)
if self.show_tracebacks:
body = format_exc()
else:
body = ""
r = bare_error(body)
response.output_status, response.header_list, response.body = r
if self.method == "HEAD":
# HEAD requests MUST NOT return a message-body in the response.
response.body = []
try:
cherrypy.log.access()
except:
cherrypy.log.error(traceback=True)
if response.timed_out:
raise cherrypy.TimeoutError()
return response
# Uncomment for stage debugging
# stage = property(lambda self: self._stage, lambda self, v: print(v))
def respond(self, path_info):
"""Generate a response for the resource at self.path_info. (Core)"""
response = cherrypy.serving.response
try:
try:
try:
if self.app is None:
raise cherrypy.NotFound()
# Get the 'Host' header, so we can HTTPRedirect properly.
self.stage = 'process_headers'
self.process_headers()
# Make a copy of the class hooks
self.hooks = self.__class__.hooks.copy()
self.toolmaps = {}
self.stage = 'get_resource'
self.get_resource(path_info)
self.body = _cpreqbody.RequestBody(
self.rfile, self.headers, request_params=self.params)
self.namespaces(self.config)
self.stage = 'on_start_resource'
self.hooks.run('on_start_resource')
# Parse the querystring
self.stage = 'process_query_string'
self.process_query_string()
# Process the body
if self.process_request_body:
if self.method not in self.methods_with_bodies:
self.process_request_body = False
self.stage = 'before_request_body'
self.hooks.run('before_request_body')
if self.process_request_body:
self.body.process()
# Run the handler
self.stage = 'before_handler'
self.hooks.run('before_handler')
if self.handler:
self.stage = 'handler'
response.body = self.handler()
# Finalize
self.stage = 'before_finalize'
self.hooks.run('before_finalize')
response.finalize()
except (cherrypy.HTTPRedirect, cherrypy.HTTPError):
inst = sys.exc_info()[1]
inst.set_response()
self.stage = 'before_finalize (HTTPError)'
self.hooks.run('before_finalize')
response.finalize()
finally:
self.stage = 'on_end_resource'
self.hooks.run('on_end_resource')
except self.throws:
raise
except:
if self.throw_errors:
raise
self.handle_error()
def process_query_string(self):
"""Parse the query string into Python structures. (Core)"""
try:
p = httputil.parse_query_string(
self.query_string, encoding=self.query_string_encoding)
except UnicodeDecodeError:
raise cherrypy.HTTPError(
404, "The given query string could not be processed. Query "
"strings for this resource must be encoded with %r." %
self.query_string_encoding)
# Python 2 only: keyword arguments must be byte strings (type 'str').
if six.PY2:
for key, value in p.items():
if isinstance(key, six.text_type):
del p[key]
p[key.encode(self.query_string_encoding)] = value
self.params.update(p)
def process_headers(self):
"""Parse HTTP header data into Python structures. (Core)"""
# Process the headers into self.headers
headers = self.headers
for name, value in self.header_list:
# Call title() now (and use dict.__method__(headers))
# so title doesn't have to be called twice.
name = name.title()
value = value.strip()
# Warning: if there is more than one header entry for cookies
# (AFAIK, only Konqueror does that), only the last one will
# remain in headers (but they will be correctly stored in
# request.cookie).
if "=?" in value:
dict.__setitem__(headers, name, httputil.decode_TEXT(value))
else:
dict.__setitem__(headers, name, value)
# Handle cookies differently because on Konqueror, multiple
# cookies come on different lines with the same key
if name == 'Cookie':
try:
self.cookie.load(value)
except CookieError:
msg = "Illegal cookie name %s" % value.split('=')[0]
raise cherrypy.HTTPError(400, msg)
if not dict.__contains__(headers, 'Host'):
# All Internet-based HTTP/1.1 servers MUST respond with a 400
# (Bad Request) status code to any HTTP/1.1 request message
# which lacks a Host header field.
if self.protocol >= (1, 1):
msg = "HTTP/1.1 requires a 'Host' request header."
raise cherrypy.HTTPError(400, msg)
host = dict.get(headers, 'Host')
if not host:
host = self.local.name or self.local.ip
self.base = "%s://%s" % (self.scheme, host)
def get_resource(self, path):
"""Call a dispatcher (which sets self.handler and .config). (Core)"""
# First, see if there is a custom dispatch at this URI. Custom
# dispatchers can only be specified in app.config, not in _cp_config
# (since custom dispatchers may not even have an app.root).
dispatch = self.app.find_config(
path, "request.dispatch", self.dispatch)
# dispatch() should set self.handler and self.config
dispatch(path)
def handle_error(self):
"""Handle the last unanticipated exception. (Core)"""
try:
self.hooks.run("before_error_response")
if self.error_response:
self.error_response()
self.hooks.run("after_error_response")
cherrypy.serving.response.finalize()
except cherrypy.HTTPRedirect:
inst = sys.exc_info()[1]
inst.set_response()
cherrypy.serving.response.finalize()
# ------------------------- Properties ------------------------- #
def _get_body_params(self):
warnings.warn(
"body_params is deprecated in CherryPy 3.2, will be removed in "
"CherryPy 3.3.",
DeprecationWarning
)
return self.body.params
body_params = property(_get_body_params,
doc="""
If the request Content-Type is 'application/x-www-form-urlencoded' or
multipart, this will be a dict of the params pulled from the entity
body; that is, it will be the portion of request.params that come
from the message body (sometimes called "POST params", although they
can be sent with various HTTP method verbs). This value is set between
the 'before_request_body' and 'before_handler' hooks (assuming that
process_request_body is True).
Deprecated in 3.2, will be removed for 3.3 in favor of
:attr:`request.body.params<cherrypy._cprequest.RequestBody.params>`.""")
class ResponseBody(object):
"""The body of the HTTP response (the response entity)."""
if six.PY3:
unicode_err = ("Page handlers MUST return bytes. Use tools.encode "
"if you wish to return unicode.")
def __get__(self, obj, objclass=None):
if obj is None:
# When calling on the class instead of an instance...
return self
else:
return obj._body
def __set__(self, obj, value):
# Convert the given value to an iterable object.
if six.PY3 and isinstance(value, str):
raise ValueError(self.unicode_err)
if isinstance(value, text_or_bytes):
# strings get wrapped in a list because iterating over a single
# item list is much faster than iterating over every character
# in a long string.
if value:
value = [value]
else:
# [''] doesn't evaluate to False, so replace it with [].
value = []
elif six.PY3 and isinstance(value, list):
# every item in a list must be bytes...
for i, item in enumerate(value):
if isinstance(item, str):
raise ValueError(self.unicode_err)
# Don't use isinstance here; io.IOBase which has an ABC takes
# 1000 times as long as, say, isinstance(value, str)
elif hasattr(value, 'read'):
value = file_generator(value)
elif value is None:
value = []
obj._body = value
class Response(object):
"""An HTTP Response, including status, headers, and body."""
status = ""
"""The HTTP Status-Code and Reason-Phrase."""
header_list = []
"""
A list of the HTTP response headers as (name, value) tuples.
In general, you should use response.headers (a dict) instead. This
attribute is generated from response.headers and is not valid until
after the finalize phase."""
headers = httputil.HeaderMap()
"""
A dict-like object containing the response headers. Keys are header
names (in Title-Case format); however, you may get and set them in
a case-insensitive manner. That is, headers['Content-Type'] and
headers['content-type'] refer to the same value. Values are header
values (decoded according to :rfc:`2047` if necessary).
.. seealso:: classes :class:`HeaderMap`, :class:`HeaderElement`
"""
cookie = SimpleCookie()
"""See help(Cookie)."""
body = ResponseBody()
"""The body (entity) of the HTTP response."""
time = None
"""The value of time.time() when created. Use in HTTP dates."""
timeout = 300
"""Seconds after which the response will be aborted."""
timed_out = False
"""
Flag to indicate the response should be aborted, because it has
exceeded its timeout."""
stream = False
"""If False, buffer the response body."""
def __init__(self):
self.status = None
self.header_list = None
self._body = []
self.time = time.time()
self.headers = httputil.HeaderMap()
# Since we know all our keys are titled strings, we can
# bypass HeaderMap.update and get a big speed boost.
dict.update(self.headers, {
"Content-Type": 'text/html',
"Server": "CherryPy/" + cherrypy.__version__,
"Date": httputil.HTTPDate(self.time),
})
self.cookie = SimpleCookie()
def collapse_body(self):
"""Collapse self.body to a single string; replace it and return it."""
if isinstance(self.body, text_or_bytes):
return self.body
newbody = []
for chunk in self.body:
if six.PY3 and not isinstance(chunk, bytes):
raise TypeError("Chunk %s is not of type 'bytes'." %
repr(chunk))
newbody.append(chunk)
newbody = ntob('').join(newbody)
self.body = newbody
return newbody
def finalize(self):
"""Transform headers (and cookies) into self.header_list. (Core)"""
try:
code, reason, _ = httputil.valid_status(self.status)
except ValueError:
raise cherrypy.HTTPError(500, sys.exc_info()[1].args[0])
headers = self.headers
self.status = "%s %s" % (code, reason)
self.output_status = ntob(str(code), 'ascii') + \
ntob(" ") + headers.encode(reason)
if self.stream:
# The upshot: wsgiserver will chunk the response if
# you pop Content-Length (or set it explicitly to None).
# Note that lib.static sets C-L to the file's st_size.
if dict.get(headers, 'Content-Length') is None:
dict.pop(headers, 'Content-Length', None)
elif code < 200 or code in (204, 205, 304):
# "All 1xx (informational), 204 (no content),
# and 304 (not modified) responses MUST NOT
# include a message-body."
dict.pop(headers, 'Content-Length', None)
self.body = ntob("")
else:
# Responses which are not streamed should have a Content-Length,
# but allow user code to set Content-Length if desired.
if dict.get(headers, 'Content-Length') is None:
content = self.collapse_body()
dict.__setitem__(headers, 'Content-Length', len(content))
# Transform our header dict into a list of tuples.
self.header_list = h = headers.output()
cookie = self.cookie.output()
if cookie:
for line in cookie.split("\n"):
if line.endswith("\r"):
# Python 2.4 emits cookies joined by LF but 2.5+ by CRLF.
line = line[:-1]
name, value = line.split(": ", 1)
if isinstance(name, six.text_type):
name = name.encode("ISO-8859-1")
if isinstance(value, six.text_type):
value = headers.encode(value)
h.append((name, value))
def check_timeout(self):
"""If now > self.time + self.timeout, set self.timed_out.
This purposefully sets a flag, rather than raising an error,
so that a monitor thread can interrupt the Response thread.
"""
if time.time() > self.time + self.timeout:
self.timed_out = True

226
deps/cherrypy/_cpserver.py vendored Normal file
View File

@@ -0,0 +1,226 @@
"""Manage HTTP servers with CherryPy."""
import six
import cherrypy
from cherrypy.lib.reprconf import attributes
from cherrypy._cpcompat import text_or_bytes
# We import * because we want to export check_port
# et al as attributes of this module.
from cherrypy.process.servers import *
class Server(ServerAdapter):
"""An adapter for an HTTP server.
You can set attributes (like socket_host and socket_port)
on *this* object (which is probably cherrypy.server), and call
quickstart. For example::
cherrypy.server.socket_port = 80
cherrypy.quickstart()
"""
socket_port = 8080
"""The TCP port on which to listen for connections."""
_socket_host = '127.0.0.1'
def _get_socket_host(self):
return self._socket_host
def _set_socket_host(self, value):
if value == '':
raise ValueError("The empty string ('') is not an allowed value. "
"Use '0.0.0.0' instead to listen on all active "
"interfaces (INADDR_ANY).")
self._socket_host = value
socket_host = property(
_get_socket_host,
_set_socket_host,
doc="""The hostname or IP address on which to listen for connections.
Host values may be any IPv4 or IPv6 address, or any valid hostname.
The string 'localhost' is a synonym for '127.0.0.1' (or '::1', if
your hosts file prefers IPv6). The string '0.0.0.0' is a special
IPv4 entry meaning "any active interface" (INADDR_ANY), and '::'
is the similar IN6ADDR_ANY for IPv6. The empty string or None are
not allowed.""")
socket_file = None
"""If given, the name of the UNIX socket to use instead of TCP/IP.
When this option is not None, the `socket_host` and `socket_port` options
are ignored."""
socket_queue_size = 5
"""The 'backlog' argument to socket.listen(); specifies the maximum number
of queued connections (default 5)."""
socket_timeout = 10
"""The timeout in seconds for accepted connections (default 10)."""
accepted_queue_size = -1
"""The maximum number of requests which will be queued up before
the server refuses to accept it (default -1, meaning no limit)."""
accepted_queue_timeout = 10
"""The timeout in seconds for attempting to add a request to the
queue when the queue is full (default 10)."""
shutdown_timeout = 5
"""The time to wait for HTTP worker threads to clean up."""
protocol_version = 'HTTP/1.1'
"""The version string to write in the Status-Line of all HTTP responses,
for example, "HTTP/1.1" (the default). Depending on the HTTP server used,
this should also limit the supported features used in the response."""
thread_pool = 10
"""The number of worker threads to start up in the pool."""
thread_pool_max = -1
"""The maximum size of the worker-thread pool. Use -1 to indicate no limit.
"""
max_request_header_size = 500 * 1024
"""The maximum number of bytes allowable in the request headers.
If exceeded, the HTTP server should return "413 Request Entity Too Large".
"""
max_request_body_size = 100 * 1024 * 1024
"""The maximum number of bytes allowable in the request body. If exceeded,
the HTTP server should return "413 Request Entity Too Large"."""
instance = None
"""If not None, this should be an HTTP server instance (such as
CPWSGIServer) which cherrypy.server will control. Use this when you need
more control over object instantiation than is available in the various
configuration options."""
ssl_context = None
"""When using PyOpenSSL, an instance of SSL.Context."""
ssl_certificate = None
"""The filename of the SSL certificate to use."""
ssl_certificate_chain = None
"""When using PyOpenSSL, the certificate chain to pass to
Context.load_verify_locations."""
ssl_private_key = None
"""The filename of the private key to use with SSL."""
if six.PY3:
ssl_module = 'builtin'
"""The name of a registered SSL adaptation module to use with
the builtin WSGI server. Builtin options are: 'builtin' (to
use the SSL library built into recent versions of Python).
You may also register your own classes in the
wsgiserver.ssl_adapters dict."""
else:
ssl_module = 'pyopenssl'
"""The name of a registered SSL adaptation module to use with the
builtin WSGI server. Builtin options are 'builtin' (to use the SSL
library built into recent versions of Python) and 'pyopenssl' (to
use the PyOpenSSL project, which you must install separately). You
may also register your own classes in the wsgiserver.ssl_adapters
dict."""
statistics = False
"""Turns statistics-gathering on or off for aware HTTP servers."""
nodelay = True
"""If True (the default since 3.1), sets the TCP_NODELAY socket option."""
wsgi_version = (1, 0)
"""The WSGI version tuple to use with the builtin WSGI server.
The provided options are (1, 0) [which includes support for PEP 3333,
which declares it covers WSGI version 1.0.1 but still mandates the
wsgi.version (1, 0)] and ('u', 0), an experimental unicode version.
You may create and register your own experimental versions of the WSGI
protocol by adding custom classes to the wsgiserver.wsgi_gateways dict."""
def __init__(self):
self.bus = cherrypy.engine
self.httpserver = None
self.interrupt = None
self.running = False
def httpserver_from_self(self, httpserver=None):
"""Return a (httpserver, bind_addr) pair based on self attributes."""
if httpserver is None:
httpserver = self.instance
if httpserver is None:
from cherrypy import _cpwsgi_server
httpserver = _cpwsgi_server.CPWSGIServer(self)
if isinstance(httpserver, text_or_bytes):
# Is anyone using this? Can I add an arg?
httpserver = attributes(httpserver)(self)
return httpserver, self.bind_addr
def start(self):
"""Start the HTTP server."""
if not self.httpserver:
self.httpserver, self.bind_addr = self.httpserver_from_self()
ServerAdapter.start(self)
start.priority = 75
def _get_bind_addr(self):
if self.socket_file:
return self.socket_file
if self.socket_host is None and self.socket_port is None:
return None
return (self.socket_host, self.socket_port)
def _set_bind_addr(self, value):
if value is None:
self.socket_file = None
self.socket_host = None
self.socket_port = None
elif isinstance(value, text_or_bytes):
self.socket_file = value
self.socket_host = None
self.socket_port = None
else:
try:
self.socket_host, self.socket_port = value
self.socket_file = None
except ValueError:
raise ValueError("bind_addr must be a (host, port) tuple "
"(for TCP sockets) or a string (for Unix "
"domain sockets), not %r" % value)
bind_addr = property(
_get_bind_addr,
_set_bind_addr,
doc='A (host, port) tuple for TCP sockets or '
'a str for Unix domain sockets.')
def base(self):
"""Return the base (scheme://host[:port] or sock file) for this server.
"""
if self.socket_file:
return self.socket_file
host = self.socket_host
if host in ('0.0.0.0', '::'):
# 0.0.0.0 is INADDR_ANY and :: is IN6ADDR_ANY.
# Look up the host name, which should be the
# safest thing to spit out in a URL.
import socket
host = socket.gethostname()
port = self.socket_port
if self.ssl_certificate:
scheme = "https"
if port != 443:
host += ":%s" % port
else:
scheme = "http"
if port != 80:
host += ":%s" % port
return "%s://%s" % (scheme, host)

241
deps/cherrypy/_cpthreadinglocal.py vendored Normal file
View File

@@ -0,0 +1,241 @@
# This is a backport of Python-2.4's threading.local() implementation
"""Thread-local objects
(Note that this module provides a Python version of thread
threading.local class. Depending on the version of Python you're
using, there may be a faster one available. You should always import
the local class from threading.)
Thread-local objects support the management of thread-local data.
If you have data that you want to be local to a thread, simply create
a thread-local object and use its attributes:
>>> mydata = local()
>>> mydata.number = 42
>>> mydata.number
42
You can also access the local-object's dictionary:
>>> mydata.__dict__
{'number': 42}
>>> mydata.__dict__.setdefault('widgets', [])
[]
>>> mydata.widgets
[]
What's important about thread-local objects is that their data are
local to a thread. If we access the data in a different thread:
>>> log = []
>>> def f():
... items = mydata.__dict__.items()
... items.sort()
... log.append(items)
... mydata.number = 11
... log.append(mydata.number)
>>> import threading
>>> thread = threading.Thread(target=f)
>>> thread.start()
>>> thread.join()
>>> log
[[], 11]
we get different data. Furthermore, changes made in the other thread
don't affect data seen in this thread:
>>> mydata.number
42
Of course, values you get from a local object, including a __dict__
attribute, are for whatever thread was current at the time the
attribute was read. For that reason, you generally don't want to save
these values across threads, as they apply only to the thread they
came from.
You can create custom local objects by subclassing the local class:
>>> class MyLocal(local):
... number = 2
... initialized = False
... def __init__(self, **kw):
... if self.initialized:
... raise SystemError('__init__ called too many times')
... self.initialized = True
... self.__dict__.update(kw)
... def squared(self):
... return self.number ** 2
This can be useful to support default values, methods and
initialization. Note that if you define an __init__ method, it will be
called each time the local object is used in a separate thread. This
is necessary to initialize each thread's dictionary.
Now if we create a local object:
>>> mydata = MyLocal(color='red')
Now we have a default number:
>>> mydata.number
2
an initial color:
>>> mydata.color
'red'
>>> del mydata.color
And a method that operates on the data:
>>> mydata.squared()
4
As before, we can access the data in a separate thread:
>>> log = []
>>> thread = threading.Thread(target=f)
>>> thread.start()
>>> thread.join()
>>> log
[[('color', 'red'), ('initialized', True)], 11]
without affecting this thread's data:
>>> mydata.number
2
>>> mydata.color
Traceback (most recent call last):
...
AttributeError: 'MyLocal' object has no attribute 'color'
Note that subclasses can define slots, but they are not thread
local. They are shared across threads:
>>> class MyLocal(local):
... __slots__ = 'number'
>>> mydata = MyLocal()
>>> mydata.number = 42
>>> mydata.color = 'red'
So, the separate thread:
>>> thread = threading.Thread(target=f)
>>> thread.start()
>>> thread.join()
affects what we see:
>>> mydata.number
11
>>> del mydata
"""
# Threading import is at end
class _localbase(object):
__slots__ = '_local__key', '_local__args', '_local__lock'
def __new__(cls, *args, **kw):
self = object.__new__(cls)
key = 'thread.local.' + str(id(self))
object.__setattr__(self, '_local__key', key)
object.__setattr__(self, '_local__args', (args, kw))
object.__setattr__(self, '_local__lock', RLock())
if args or kw and (cls.__init__ is object.__init__):
raise TypeError("Initialization arguments are not supported")
# We need to create the thread dict in anticipation of
# __init__ being called, to make sure we don't call it
# again ourselves.
dict = object.__getattribute__(self, '__dict__')
currentThread().__dict__[key] = dict
return self
def _patch(self):
key = object.__getattribute__(self, '_local__key')
d = currentThread().__dict__.get(key)
if d is None:
d = {}
currentThread().__dict__[key] = d
object.__setattr__(self, '__dict__', d)
# we have a new instance dict, so call out __init__ if we have
# one
cls = type(self)
if cls.__init__ is not object.__init__:
args, kw = object.__getattribute__(self, '_local__args')
cls.__init__(self, *args, **kw)
else:
object.__setattr__(self, '__dict__', d)
class local(_localbase):
def __getattribute__(self, name):
lock = object.__getattribute__(self, '_local__lock')
lock.acquire()
try:
_patch(self)
return object.__getattribute__(self, name)
finally:
lock.release()
def __setattr__(self, name, value):
lock = object.__getattribute__(self, '_local__lock')
lock.acquire()
try:
_patch(self)
return object.__setattr__(self, name, value)
finally:
lock.release()
def __delattr__(self, name):
lock = object.__getattribute__(self, '_local__lock')
lock.acquire()
try:
_patch(self)
return object.__delattr__(self, name)
finally:
lock.release()
def __del__():
threading_enumerate = enumerate
__getattribute__ = object.__getattribute__
def __del__(self):
key = __getattribute__(self, '_local__key')
try:
threads = list(threading_enumerate())
except:
# if enumerate fails, as it seems to do during
# shutdown, we'll skip cleanup under the assumption
# that there is nothing to clean up
return
for thread in threads:
try:
__dict__ = thread.__dict__
except AttributeError:
# Thread is dying, rest in peace
continue
if key in __dict__:
try:
del __dict__[key]
except KeyError:
pass # didn't have anything in this thread
return __del__
__del__ = __del__()
from threading import currentThread, enumerate, RLock

538
deps/cherrypy/_cptools.py vendored Normal file
View File

@@ -0,0 +1,538 @@
"""CherryPy tools. A "tool" is any helper, adapted to CP.
Tools are usually designed to be used in a variety of ways (although some
may only offer one if they choose):
Library calls
All tools are callables that can be used wherever needed.
The arguments are straightforward and should be detailed within the
docstring.
Function decorators
All tools, when called, may be used as decorators which configure
individual CherryPy page handlers (methods on the CherryPy tree).
That is, "@tools.anytool()" should "turn on" the tool via the
decorated function's _cp_config attribute.
CherryPy config
If a tool exposes a "_setup" callable, it will be called
once per Request (if the feature is "turned on" via config).
Tools may be implemented as any object with a namespace. The builtins
are generally either modules or instances of the tools.Tool class.
"""
import sys
import warnings
import cherrypy
from cherrypy._helper import expose
def _getargs(func):
"""Return the names of all static arguments to the given function."""
# Use this instead of importing inspect for less mem overhead.
import types
if sys.version_info >= (3, 0):
if isinstance(func, types.MethodType):
func = func.__func__
co = func.__code__
else:
if isinstance(func, types.MethodType):
func = func.im_func
co = func.func_code
return co.co_varnames[:co.co_argcount]
_attr_error = (
"CherryPy Tools cannot be turned on directly. Instead, turn them "
"on via config, or use them as decorators on your page handlers."
)
class Tool(object):
"""A registered function for use with CherryPy request-processing hooks.
help(tool.callable) should give you more information about this Tool.
"""
namespace = "tools"
def __init__(self, point, callable, name=None, priority=50):
self._point = point
self.callable = callable
self._name = name
self._priority = priority
self.__doc__ = self.callable.__doc__
self._setargs()
def _get_on(self):
raise AttributeError(_attr_error)
def _set_on(self, value):
raise AttributeError(_attr_error)
on = property(_get_on, _set_on)
def _setargs(self):
"""Copy func parameter names to obj attributes."""
try:
for arg in _getargs(self.callable):
setattr(self, arg, None)
except (TypeError, AttributeError):
if hasattr(self.callable, "__call__"):
for arg in _getargs(self.callable.__call__):
setattr(self, arg, None)
# IronPython 1.0 raises NotImplementedError because
# inspect.getargspec tries to access Python bytecode
# in co_code attribute.
except NotImplementedError:
pass
# IronPython 1B1 may raise IndexError in some cases,
# but if we trap it here it doesn't prevent CP from working.
except IndexError:
pass
def _merged_args(self, d=None):
"""Return a dict of configuration entries for this Tool."""
if d:
conf = d.copy()
else:
conf = {}
tm = cherrypy.serving.request.toolmaps[self.namespace]
if self._name in tm:
conf.update(tm[self._name])
if "on" in conf:
del conf["on"]
return conf
def __call__(self, *args, **kwargs):
"""Compile-time decorator (turn on the tool in config).
For example::
@expose
@tools.proxy()
def whats_my_base(self):
return cherrypy.request.base
"""
if args:
raise TypeError("The %r Tool does not accept positional "
"arguments; you must use keyword arguments."
% self._name)
def tool_decorator(f):
if not hasattr(f, "_cp_config"):
f._cp_config = {}
subspace = self.namespace + "." + self._name + "."
f._cp_config[subspace + "on"] = True
for k, v in kwargs.items():
f._cp_config[subspace + k] = v
return f
return tool_decorator
def _setup(self):
"""Hook this tool into cherrypy.request.
The standard CherryPy request object will automatically call this
method when the tool is "turned on" in config.
"""
conf = self._merged_args()
p = conf.pop("priority", None)
if p is None:
p = getattr(self.callable, "priority", self._priority)
cherrypy.serving.request.hooks.attach(self._point, self.callable,
priority=p, **conf)
class HandlerTool(Tool):
"""Tool which is called 'before main', that may skip normal handlers.
If the tool successfully handles the request (by setting response.body),
if should return True. This will cause CherryPy to skip any 'normal' page
handler. If the tool did not handle the request, it should return False
to tell CherryPy to continue on and call the normal page handler. If the
tool is declared AS a page handler (see the 'handler' method), returning
False will raise NotFound.
"""
def __init__(self, callable, name=None):
Tool.__init__(self, 'before_handler', callable, name)
def handler(self, *args, **kwargs):
"""Use this tool as a CherryPy page handler.
For example::
class Root:
nav = tools.staticdir.handler(section="/nav", dir="nav",
root=absDir)
"""
@expose
def handle_func(*a, **kw):
handled = self.callable(*args, **self._merged_args(kwargs))
if not handled:
raise cherrypy.NotFound()
return cherrypy.serving.response.body
return handle_func
def _wrapper(self, **kwargs):
if self.callable(**kwargs):
cherrypy.serving.request.handler = None
def _setup(self):
"""Hook this tool into cherrypy.request.
The standard CherryPy request object will automatically call this
method when the tool is "turned on" in config.
"""
conf = self._merged_args()
p = conf.pop("priority", None)
if p is None:
p = getattr(self.callable, "priority", self._priority)
cherrypy.serving.request.hooks.attach(self._point, self._wrapper,
priority=p, **conf)
class HandlerWrapperTool(Tool):
"""Tool which wraps request.handler in a provided wrapper function.
The 'newhandler' arg must be a handler wrapper function that takes a
'next_handler' argument, plus ``*args`` and ``**kwargs``. Like all
page handler
functions, it must return an iterable for use as cherrypy.response.body.
For example, to allow your 'inner' page handlers to return dicts
which then get interpolated into a template::
def interpolator(next_handler, *args, **kwargs):
filename = cherrypy.request.config.get('template')
cherrypy.response.template = env.get_template(filename)
response_dict = next_handler(*args, **kwargs)
return cherrypy.response.template.render(**response_dict)
cherrypy.tools.jinja = HandlerWrapperTool(interpolator)
"""
def __init__(self, newhandler, point='before_handler', name=None,
priority=50):
self.newhandler = newhandler
self._point = point
self._name = name
self._priority = priority
def callable(self, *args, **kwargs):
innerfunc = cherrypy.serving.request.handler
def wrap(*args, **kwargs):
return self.newhandler(innerfunc, *args, **kwargs)
cherrypy.serving.request.handler = wrap
class ErrorTool(Tool):
"""Tool which is used to replace the default request.error_response."""
def __init__(self, callable, name=None):
Tool.__init__(self, None, callable, name)
def _wrapper(self):
self.callable(**self._merged_args())
def _setup(self):
"""Hook this tool into cherrypy.request.
The standard CherryPy request object will automatically call this
method when the tool is "turned on" in config.
"""
cherrypy.serving.request.error_response = self._wrapper
# Builtin tools #
from cherrypy.lib import cptools, encoding, auth, static, jsontools
from cherrypy.lib import sessions as _sessions, xmlrpcutil as _xmlrpc
from cherrypy.lib import caching as _caching
from cherrypy.lib import auth_basic, auth_digest
class SessionTool(Tool):
"""Session Tool for CherryPy.
sessions.locking
When 'implicit' (the default), the session will be locked for you,
just before running the page handler.
When 'early', the session will be locked before reading the request
body. This is off by default for safety reasons; for example,
a large upload would block the session, denying an AJAX
progress meter
(`issue <https://github.com/cherrypy/cherrypy/issues/630>`_).
When 'explicit' (or any other value), you need to call
cherrypy.session.acquire_lock() yourself before using
session data.
"""
def __init__(self):
# _sessions.init must be bound after headers are read
Tool.__init__(self, 'before_request_body', _sessions.init)
def _lock_session(self):
cherrypy.serving.session.acquire_lock()
def _setup(self):
"""Hook this tool into cherrypy.request.
The standard CherryPy request object will automatically call this
method when the tool is "turned on" in config.
"""
hooks = cherrypy.serving.request.hooks
conf = self._merged_args()
p = conf.pop("priority", None)
if p is None:
p = getattr(self.callable, "priority", self._priority)
hooks.attach(self._point, self.callable, priority=p, **conf)
locking = conf.pop('locking', 'implicit')
if locking == 'implicit':
hooks.attach('before_handler', self._lock_session)
elif locking == 'early':
# Lock before the request body (but after _sessions.init runs!)
hooks.attach('before_request_body', self._lock_session,
priority=60)
else:
# Don't lock
pass
hooks.attach('before_finalize', _sessions.save)
hooks.attach('on_end_request', _sessions.close)
def regenerate(self):
"""Drop the current session and make a new one (with a new id)."""
sess = cherrypy.serving.session
sess.regenerate()
# Grab cookie-relevant tool args
conf = dict([(k, v) for k, v in self._merged_args().items()
if k in ('path', 'path_header', 'name', 'timeout',
'domain', 'secure')])
_sessions.set_response_cookie(**conf)
class XMLRPCController(object):
"""A Controller (page handler collection) for XML-RPC.
To use it, have your controllers subclass this base class (it will
turn on the tool for you).
You can also supply the following optional config entries::
tools.xmlrpc.encoding: 'utf-8'
tools.xmlrpc.allow_none: 0
XML-RPC is a rather discontinuous layer over HTTP; dispatching to the
appropriate handler must first be performed according to the URL, and
then a second dispatch step must take place according to the RPC method
specified in the request body. It also allows a superfluous "/RPC2"
prefix in the URL, supplies its own handler args in the body, and
requires a 200 OK "Fault" response instead of 404 when the desired
method is not found.
Therefore, XML-RPC cannot be implemented for CherryPy via a Tool alone.
This Controller acts as the dispatch target for the first half (based
on the URL); it then reads the RPC method from the request body and
does its own second dispatch step based on that method. It also reads
body params, and returns a Fault on error.
The XMLRPCDispatcher strips any /RPC2 prefix; if you aren't using /RPC2
in your URL's, you can safely skip turning on the XMLRPCDispatcher.
Otherwise, you need to use declare it in config::
request.dispatch: cherrypy.dispatch.XMLRPCDispatcher()
"""
# Note we're hard-coding this into the 'tools' namespace. We could do
# a huge amount of work to make it relocatable, but the only reason why
# would be if someone actually disabled the default_toolbox. Meh.
_cp_config = {'tools.xmlrpc.on': True}
@expose
def default(self, *vpath, **params):
rpcparams, rpcmethod = _xmlrpc.process_body()
subhandler = self
for attr in str(rpcmethod).split('.'):
subhandler = getattr(subhandler, attr, None)
if subhandler and getattr(subhandler, "exposed", False):
body = subhandler(*(vpath + rpcparams), **params)
else:
# https://github.com/cherrypy/cherrypy/issues/533
# if a method is not found, an xmlrpclib.Fault should be returned
# raising an exception here will do that; see
# cherrypy.lib.xmlrpcutil.on_error
raise Exception('method "%s" is not supported' % attr)
conf = cherrypy.serving.request.toolmaps['tools'].get("xmlrpc", {})
_xmlrpc.respond(body,
conf.get('encoding', 'utf-8'),
conf.get('allow_none', 0))
return cherrypy.serving.response.body
class SessionAuthTool(HandlerTool):
def _setargs(self):
for name in dir(cptools.SessionAuth):
if not name.startswith("__"):
setattr(self, name, None)
class CachingTool(Tool):
"""Caching Tool for CherryPy."""
def _wrapper(self, **kwargs):
request = cherrypy.serving.request
if _caching.get(**kwargs):
request.handler = None
else:
if request.cacheable:
# Note the devious technique here of adding hooks on the fly
request.hooks.attach('before_finalize', _caching.tee_output,
priority=90)
_wrapper.priority = 20
def _setup(self):
"""Hook caching into cherrypy.request."""
conf = self._merged_args()
p = conf.pop("priority", None)
cherrypy.serving.request.hooks.attach('before_handler', self._wrapper,
priority=p, **conf)
class Toolbox(object):
"""A collection of Tools.
This object also functions as a config namespace handler for itself.
Custom toolboxes should be added to each Application's toolboxes dict.
"""
def __init__(self, namespace):
self.namespace = namespace
def __setattr__(self, name, value):
# If the Tool._name is None, supply it from the attribute name.
if isinstance(value, Tool):
if value._name is None:
value._name = name
value.namespace = self.namespace
object.__setattr__(self, name, value)
def __enter__(self):
"""Populate request.toolmaps from tools specified in config."""
cherrypy.serving.request.toolmaps[self.namespace] = map = {}
def populate(k, v):
toolname, arg = k.split(".", 1)
bucket = map.setdefault(toolname, {})
bucket[arg] = v
return populate
def __exit__(self, exc_type, exc_val, exc_tb):
"""Run tool._setup() for each tool in our toolmap."""
map = cherrypy.serving.request.toolmaps.get(self.namespace)
if map:
for name, settings in map.items():
if settings.get("on", False):
tool = getattr(self, name)
tool._setup()
def register(self, point, **kwargs):
"""Return a decorator which registers the function at the given hook point."""
def decorator(func):
setattr(self, kwargs.get('name', func.__name__), Tool(point, func, **kwargs))
return func
return decorator
class DeprecatedTool(Tool):
_name = None
warnmsg = "This Tool is deprecated."
def __init__(self, point, warnmsg=None):
self.point = point
if warnmsg is not None:
self.warnmsg = warnmsg
def __call__(self, *args, **kwargs):
warnings.warn(self.warnmsg)
def tool_decorator(f):
return f
return tool_decorator
def _setup(self):
warnings.warn(self.warnmsg)
default_toolbox = _d = Toolbox("tools")
_d.session_auth = SessionAuthTool(cptools.session_auth)
_d.allow = Tool('on_start_resource', cptools.allow)
_d.proxy = Tool('before_request_body', cptools.proxy, priority=30)
_d.response_headers = Tool('on_start_resource', cptools.response_headers)
_d.log_tracebacks = Tool('before_error_response', cptools.log_traceback)
_d.log_headers = Tool('before_error_response', cptools.log_request_headers)
_d.log_hooks = Tool('on_end_request', cptools.log_hooks, priority=100)
_d.err_redirect = ErrorTool(cptools.redirect)
_d.etags = Tool('before_finalize', cptools.validate_etags, priority=75)
_d.decode = Tool('before_request_body', encoding.decode)
# the order of encoding, gzip, caching is important
_d.encode = Tool('before_handler', encoding.ResponseEncoder, priority=70)
_d.gzip = Tool('before_finalize', encoding.gzip, priority=80)
_d.staticdir = HandlerTool(static.staticdir)
_d.staticfile = HandlerTool(static.staticfile)
_d.sessions = SessionTool()
_d.xmlrpc = ErrorTool(_xmlrpc.on_error)
_d.caching = CachingTool('before_handler', _caching.get, 'caching')
_d.expires = Tool('before_finalize', _caching.expires)
_d.tidy = DeprecatedTool(
'before_finalize',
"The tidy tool has been removed from the standard distribution of "
"CherryPy. The most recent version can be found at "
"http://tools.cherrypy.org/browser.")
_d.nsgmls = DeprecatedTool(
'before_finalize',
"The nsgmls tool has been removed from the standard distribution of "
"CherryPy. The most recent version can be found at "
"http://tools.cherrypy.org/browser.")
_d.ignore_headers = Tool('before_request_body', cptools.ignore_headers)
_d.referer = Tool('before_request_body', cptools.referer)
_d.basic_auth = Tool('on_start_resource', auth.basic_auth)
_d.digest_auth = Tool('on_start_resource', auth.digest_auth)
_d.trailing_slash = Tool('before_handler', cptools.trailing_slash, priority=60)
_d.flatten = Tool('before_finalize', cptools.flatten)
_d.accept = Tool('on_start_resource', cptools.accept)
_d.redirect = Tool('on_start_resource', cptools.redirect)
_d.autovary = Tool('on_start_resource', cptools.autovary, priority=0)
_d.json_in = Tool('before_request_body', jsontools.json_in, priority=30)
_d.json_out = Tool('before_handler', jsontools.json_out, priority=30)
_d.auth_basic = Tool('before_handler', auth_basic.basic_auth, priority=1)
_d.auth_digest = Tool('before_handler', auth_digest.digest_auth, priority=1)
_d.params = Tool('before_handler', cptools.convert_params)
del _d, cptools, encoding, auth, static

287
deps/cherrypy/_cptree.py vendored Normal file
View File

@@ -0,0 +1,287 @@
"""CherryPy Application and Tree objects."""
import os
import six
import cherrypy
from cherrypy._cpcompat import ntou
from cherrypy import _cpconfig, _cplogging, _cprequest, _cpwsgi, tools
from cherrypy.lib import httputil
class Application(object):
"""A CherryPy Application.
Servers and gateways should not instantiate Request objects directly.
Instead, they should ask an Application object for a request object.
An instance of this class may also be used as a WSGI callable
(WSGI application object) for itself.
"""
root = None
"""The top-most container of page handlers for this app. Handlers should
be arranged in a hierarchy of attributes, matching the expected URI
hierarchy; the default dispatcher then searches this hierarchy for a
matching handler. When using a dispatcher other than the default,
this value may be None."""
config = {}
"""A dict of {path: pathconf} pairs, where 'pathconf' is itself a dict
of {key: value} pairs."""
namespaces = _cpconfig.NamespaceSet()
toolboxes = {'tools': cherrypy.tools}
log = None
"""A LogManager instance. See _cplogging."""
wsgiapp = None
"""A CPWSGIApp instance. See _cpwsgi."""
request_class = _cprequest.Request
response_class = _cprequest.Response
relative_urls = False
def __init__(self, root, script_name="", config=None):
self.log = _cplogging.LogManager(id(self), cherrypy.log.logger_root)
self.root = root
self.script_name = script_name
self.wsgiapp = _cpwsgi.CPWSGIApp(self)
self.namespaces = self.namespaces.copy()
self.namespaces["log"] = lambda k, v: setattr(self.log, k, v)
self.namespaces["wsgi"] = self.wsgiapp.namespace_handler
self.config = self.__class__.config.copy()
if config:
self.merge(config)
def __repr__(self):
return "%s.%s(%r, %r)" % (self.__module__, self.__class__.__name__,
self.root, self.script_name)
script_name_doc = """The URI "mount point" for this app. A mount point
is that portion of the URI which is constant for all URIs that are
serviced by this application; it does not include scheme, host, or proxy
("virtual host") portions of the URI.
For example, if script_name is "/my/cool/app", then the URL
"http://www.example.com/my/cool/app/page1" might be handled by a
"page1" method on the root object.
The value of script_name MUST NOT end in a slash. If the script_name
refers to the root of the URI, it MUST be an empty string (not "/").
If script_name is explicitly set to None, then the script_name will be
provided for each call from request.wsgi_environ['SCRIPT_NAME'].
"""
def _get_script_name(self):
if self._script_name is not None:
return self._script_name
# A `_script_name` with a value of None signals that the script name
# should be pulled from WSGI environ.
return cherrypy.serving.request.wsgi_environ['SCRIPT_NAME'].rstrip("/")
def _set_script_name(self, value):
if value:
value = value.rstrip("/")
self._script_name = value
script_name = property(fget=_get_script_name, fset=_set_script_name,
doc=script_name_doc)
def merge(self, config):
"""Merge the given config into self.config."""
_cpconfig.merge(self.config, config)
# Handle namespaces specified in config.
self.namespaces(self.config.get("/", {}))
def find_config(self, path, key, default=None):
"""Return the most-specific value for key along path, or default."""
trail = path or "/"
while trail:
nodeconf = self.config.get(trail, {})
if key in nodeconf:
return nodeconf[key]
lastslash = trail.rfind("/")
if lastslash == -1:
break
elif lastslash == 0 and trail != "/":
trail = "/"
else:
trail = trail[:lastslash]
return default
def get_serving(self, local, remote, scheme, sproto):
"""Create and return a Request and Response object."""
req = self.request_class(local, remote, scheme, sproto)
req.app = self
for name, toolbox in self.toolboxes.items():
req.namespaces[name] = toolbox
resp = self.response_class()
cherrypy.serving.load(req, resp)
cherrypy.engine.publish('acquire_thread')
cherrypy.engine.publish('before_request')
return req, resp
def release_serving(self):
"""Release the current serving (request and response)."""
req = cherrypy.serving.request
cherrypy.engine.publish('after_request')
try:
req.close()
except:
cherrypy.log(traceback=True, severity=40)
cherrypy.serving.clear()
def __call__(self, environ, start_response):
return self.wsgiapp(environ, start_response)
class Tree(object):
"""A registry of CherryPy applications, mounted at diverse points.
An instance of this class may also be used as a WSGI callable
(WSGI application object), in which case it dispatches to all
mounted apps.
"""
apps = {}
"""
A dict of the form {script name: application}, where "script name"
is a string declaring the URI mount point (no trailing slash), and
"application" is an instance of cherrypy.Application (or an arbitrary
WSGI callable if you happen to be using a WSGI server)."""
def __init__(self):
self.apps = {}
def mount(self, root, script_name="", config=None):
"""Mount a new app from a root object, script_name, and config.
root
An instance of a "controller class" (a collection of page
handler methods) which represents the root of the application.
This may also be an Application instance, or None if using
a dispatcher other than the default.
script_name
A string containing the "mount point" of the application.
This should start with a slash, and be the path portion of the
URL at which to mount the given root. For example, if root.index()
will handle requests to "http://www.example.com:8080/dept/app1/",
then the script_name argument would be "/dept/app1".
It MUST NOT end in a slash. If the script_name refers to the
root of the URI, it MUST be an empty string (not "/").
config
A file or dict containing application config.
"""
if script_name is None:
raise TypeError(
"The 'script_name' argument may not be None. Application "
"objects may, however, possess a script_name of None (in "
"order to inpect the WSGI environ for SCRIPT_NAME upon each "
"request). You cannot mount such Applications on this Tree; "
"you must pass them to a WSGI server interface directly.")
# Next line both 1) strips trailing slash and 2) maps "/" -> "".
script_name = script_name.rstrip("/")
if isinstance(root, Application):
app = root
if script_name != "" and script_name != app.script_name:
raise ValueError(
"Cannot specify a different script name and pass an "
"Application instance to cherrypy.mount")
script_name = app.script_name
else:
app = Application(root, script_name)
# If mounted at "", add favicon.ico
if (script_name == "" and root is not None
and not hasattr(root, "favicon_ico")):
favicon = os.path.join(os.getcwd(), os.path.dirname(__file__),
"favicon.ico")
root.favicon_ico = tools.staticfile.handler(favicon)
if config:
app.merge(config)
self.apps[script_name] = app
return app
def graft(self, wsgi_callable, script_name=""):
"""Mount a wsgi callable at the given script_name."""
# Next line both 1) strips trailing slash and 2) maps "/" -> "".
script_name = script_name.rstrip("/")
self.apps[script_name] = wsgi_callable
def script_name(self, path=None):
"""The script_name of the app at the given path, or None.
If path is None, cherrypy.request is used.
"""
if path is None:
try:
request = cherrypy.serving.request
path = httputil.urljoin(request.script_name,
request.path_info)
except AttributeError:
return None
while True:
if path in self.apps:
return path
if path == "":
return None
# Move one node up the tree and try again.
path = path[:path.rfind("/")]
def __call__(self, environ, start_response):
# If you're calling this, then you're probably setting SCRIPT_NAME
# to '' (some WSGI servers always set SCRIPT_NAME to '').
# Try to look up the app using the full path.
env1x = environ
if six.PY2 and environ.get(ntou('wsgi.version')) == (ntou('u'), 0):
env1x = _cpwsgi.downgrade_wsgi_ux_to_1x(environ)
path = httputil.urljoin(env1x.get('SCRIPT_NAME', ''),
env1x.get('PATH_INFO', ''))
sn = self.script_name(path or "/")
if sn is None:
start_response('404 Not Found', [])
return []
app = self.apps[sn]
# Correct the SCRIPT_NAME and PATH_INFO environ entries.
environ = environ.copy()
if six.PY2 and environ.get(ntou('wsgi.version')) == (ntou('u'), 0):
# Python 2/WSGI u.0: all strings MUST be of type unicode
enc = environ[ntou('wsgi.url_encoding')]
environ[ntou('SCRIPT_NAME')] = sn.decode(enc)
environ[ntou('PATH_INFO')] = path[len(sn.rstrip("/")):].decode(enc)
else:
environ['SCRIPT_NAME'] = sn
environ['PATH_INFO'] = path[len(sn.rstrip("/")):]
return app(environ, start_response)

467
deps/cherrypy/_cpwsgi.py vendored Normal file
View File

@@ -0,0 +1,467 @@
"""WSGI interface (see PEP 333 and 3333).
Note that WSGI environ keys and values are 'native strings'; that is,
whatever the type of "" is. For Python 2, that's a byte string; for Python 3,
it's a unicode string. But PEP 3333 says: "even if Python's str type is
actually Unicode "under the hood", the content of native strings must
still be translatable to bytes via the Latin-1 encoding!"
"""
import sys as _sys
import io
import six
import cherrypy as _cherrypy
from cherrypy._cpcompat import ntob, ntou
from cherrypy import _cperror
from cherrypy.lib import httputil
from cherrypy.lib import is_closable_iterator
def downgrade_wsgi_ux_to_1x(environ):
"""Return a new environ dict for WSGI 1.x from the given WSGI u.x environ.
"""
env1x = {}
url_encoding = environ[ntou('wsgi.url_encoding')]
for k, v in list(environ.items()):
if k in [ntou('PATH_INFO'), ntou('SCRIPT_NAME'), ntou('QUERY_STRING')]:
v = v.encode(url_encoding)
elif isinstance(v, six.text_type):
v = v.encode('ISO-8859-1')
env1x[k.encode('ISO-8859-1')] = v
return env1x
class VirtualHost(object):
"""Select a different WSGI application based on the Host header.
This can be useful when running multiple sites within one CP server.
It allows several domains to point to different applications. For example::
root = Root()
RootApp = cherrypy.Application(root)
Domain2App = cherrypy.Application(root)
SecureApp = cherrypy.Application(Secure())
vhost = cherrypy._cpwsgi.VirtualHost(
RootApp,
domains={
'www.domain2.example': Domain2App,
'www.domain2.example:443': SecureApp,
},
)
cherrypy.tree.graft(vhost)
"""
default = None
"""Required. The default WSGI application."""
use_x_forwarded_host = True
"""If True (the default), any "X-Forwarded-Host"
request header will be used instead of the "Host" header. This
is commonly added by HTTP servers (such as Apache) when proxying."""
domains = {}
"""A dict of {host header value: application} pairs.
The incoming "Host" request header is looked up in this dict,
and, if a match is found, the corresponding WSGI application
will be called instead of the default. Note that you often need
separate entries for "example.com" and "www.example.com".
In addition, "Host" headers may contain the port number.
"""
def __init__(self, default, domains=None, use_x_forwarded_host=True):
self.default = default
self.domains = domains or {}
self.use_x_forwarded_host = use_x_forwarded_host
def __call__(self, environ, start_response):
domain = environ.get('HTTP_HOST', '')
if self.use_x_forwarded_host:
domain = environ.get("HTTP_X_FORWARDED_HOST", domain)
nextapp = self.domains.get(domain)
if nextapp is None:
nextapp = self.default
return nextapp(environ, start_response)
class InternalRedirector(object):
"""WSGI middleware that handles raised cherrypy.InternalRedirect."""
def __init__(self, nextapp, recursive=False):
self.nextapp = nextapp
self.recursive = recursive
def __call__(self, environ, start_response):
redirections = []
while True:
environ = environ.copy()
try:
return self.nextapp(environ, start_response)
except _cherrypy.InternalRedirect:
ir = _sys.exc_info()[1]
sn = environ.get('SCRIPT_NAME', '')
path = environ.get('PATH_INFO', '')
qs = environ.get('QUERY_STRING', '')
# Add the *previous* path_info + qs to redirections.
old_uri = sn + path
if qs:
old_uri += "?" + qs
redirections.append(old_uri)
if not self.recursive:
# Check to see if the new URI has been redirected to
# already
new_uri = sn + ir.path
if ir.query_string:
new_uri += "?" + ir.query_string
if new_uri in redirections:
ir.request.close()
tmpl = (
"InternalRedirector visited the same URL twice: %r"
)
raise RuntimeError(tmpl % new_uri)
# Munge the environment and try again.
environ['REQUEST_METHOD'] = "GET"
environ['PATH_INFO'] = ir.path
environ['QUERY_STRING'] = ir.query_string
environ['wsgi.input'] = io.BytesIO()
environ['CONTENT_LENGTH'] = "0"
environ['cherrypy.previous_request'] = ir.request
class ExceptionTrapper(object):
"""WSGI middleware that traps exceptions."""
def __init__(self, nextapp, throws=(KeyboardInterrupt, SystemExit)):
self.nextapp = nextapp
self.throws = throws
def __call__(self, environ, start_response):
return _TrappedResponse(
self.nextapp,
environ,
start_response,
self.throws
)
class _TrappedResponse(object):
response = iter([])
def __init__(self, nextapp, environ, start_response, throws):
self.nextapp = nextapp
self.environ = environ
self.start_response = start_response
self.throws = throws
self.started_response = False
self.response = self.trap(
self.nextapp, self.environ, self.start_response,
)
self.iter_response = iter(self.response)
def __iter__(self):
self.started_response = True
return self
def __next__(self):
return self.trap(next, self.iter_response)
# todo: https://pythonhosted.org/six/#six.Iterator
if six.PY2:
next = __next__
def close(self):
if hasattr(self.response, 'close'):
self.response.close()
def trap(self, func, *args, **kwargs):
try:
return func(*args, **kwargs)
except self.throws:
raise
except StopIteration:
raise
except:
tb = _cperror.format_exc()
#print('trapped (started %s):' % self.started_response, tb)
_cherrypy.log(tb, severity=40)
if not _cherrypy.request.show_tracebacks:
tb = ""
s, h, b = _cperror.bare_error(tb)
if six.PY3:
# What fun.
s = s.decode('ISO-8859-1')
h = [
(k.decode('ISO-8859-1'), v.decode('ISO-8859-1'))
for k, v in h
]
if self.started_response:
# Empty our iterable (so future calls raise StopIteration)
self.iter_response = iter([])
else:
self.iter_response = iter(b)
try:
self.start_response(s, h, _sys.exc_info())
except:
# "The application must not trap any exceptions raised by
# start_response, if it called start_response with exc_info.
# Instead, it should allow such exceptions to propagate
# back to the server or gateway."
# But we still log and call close() to clean up ourselves.
_cherrypy.log(traceback=True, severity=40)
raise
if self.started_response:
return ntob("").join(b)
else:
return b
# WSGI-to-CP Adapter #
class AppResponse(object):
"""WSGI response iterable for CherryPy applications."""
def __init__(self, environ, start_response, cpapp):
self.cpapp = cpapp
try:
if six.PY2:
if environ.get(ntou('wsgi.version')) == (ntou('u'), 0):
environ = downgrade_wsgi_ux_to_1x(environ)
self.environ = environ
self.run()
r = _cherrypy.serving.response
outstatus = r.output_status
if not isinstance(outstatus, bytes):
raise TypeError("response.output_status is not a byte string.")
outheaders = []
for k, v in r.header_list:
if not isinstance(k, bytes):
tmpl = "response.header_list key %r is not a byte string."
raise TypeError(tmpl % k)
if not isinstance(v, bytes):
tmpl = (
"response.header_list value %r is not a byte string."
)
raise TypeError(tmpl % v)
outheaders.append((k, v))
if six.PY3:
# According to PEP 3333, when using Python 3, the response
# status and headers must be bytes masquerading as unicode;
# that is, they must be of type "str" but are restricted to
# code points in the "latin-1" set.
outstatus = outstatus.decode('ISO-8859-1')
outheaders = [
(k.decode('ISO-8859-1'), v.decode('ISO-8859-1'))
for k, v in outheaders
]
self.iter_response = iter(r.body)
self.write = start_response(outstatus, outheaders)
except:
self.close()
raise
def __iter__(self):
return self
def __next__(self):
return next(self.iter_response)
# todo: https://pythonhosted.org/six/#six.Iterator
if six.PY2:
next = __next__
def close(self):
"""Close and de-reference the current request and response. (Core)"""
streaming = _cherrypy.serving.response.stream
self.cpapp.release_serving()
# We avoid the expense of examining the iterator to see if it's
# closable unless we are streaming the response, as that's the
# only situation where we are going to have an iterator which
# may not have been exhausted yet.
if streaming and is_closable_iterator(self.iter_response):
iter_close = self.iter_response.close
try:
iter_close()
except Exception:
_cherrypy.log(traceback=True, severity=40)
def run(self):
"""Create a Request object using environ."""
env = self.environ.get
local = httputil.Host(
'',
int(env('SERVER_PORT', 80) or -1),
env('SERVER_NAME', ''),
)
remote = httputil.Host(
env('REMOTE_ADDR', ''),
int(env('REMOTE_PORT', -1) or -1),
env('REMOTE_HOST', ''),
)
scheme = env('wsgi.url_scheme')
sproto = env('ACTUAL_SERVER_PROTOCOL', "HTTP/1.1")
request, resp = self.cpapp.get_serving(local, remote, scheme, sproto)
# LOGON_USER is served by IIS, and is the name of the
# user after having been mapped to a local account.
# Both IIS and Apache set REMOTE_USER, when possible.
request.login = env('LOGON_USER') or env('REMOTE_USER') or None
request.multithread = self.environ['wsgi.multithread']
request.multiprocess = self.environ['wsgi.multiprocess']
request.wsgi_environ = self.environ
request.prev = env('cherrypy.previous_request', None)
meth = self.environ['REQUEST_METHOD']
path = httputil.urljoin(
self.environ.get('SCRIPT_NAME', ''),
self.environ.get('PATH_INFO', ''),
)
qs = self.environ.get('QUERY_STRING', '')
path, qs = self.recode_path_qs(path, qs) or (path, qs)
rproto = self.environ.get('SERVER_PROTOCOL')
headers = self.translate_headers(self.environ)
rfile = self.environ['wsgi.input']
request.run(meth, path, qs, rproto, headers, rfile)
headerNames = {
'HTTP_CGI_AUTHORIZATION': 'Authorization',
'CONTENT_LENGTH': 'Content-Length',
'CONTENT_TYPE': 'Content-Type',
'REMOTE_HOST': 'Remote-Host',
'REMOTE_ADDR': 'Remote-Addr',
}
def recode_path_qs(self, path, qs):
if not six.PY3:
return
# This isn't perfect; if the given PATH_INFO is in the
# wrong encoding, it may fail to match the appropriate config
# section URI. But meh.
old_enc = self.environ.get('wsgi.url_encoding', 'ISO-8859-1')
new_enc = self.cpapp.find_config(
self.environ.get('PATH_INFO', ''),
"request.uri_encoding", 'utf-8',
)
if new_enc.lower() == old_enc.lower():
return
# Even though the path and qs are unicode, the WSGI server
# is required by PEP 3333 to coerce them to ISO-8859-1
# masquerading as unicode. So we have to encode back to
# bytes and then decode again using the "correct" encoding.
try:
return (
path.encode(old_enc).decode(new_enc),
qs.encode(old_enc).decode(new_enc),
)
except (UnicodeEncodeError, UnicodeDecodeError):
# Just pass them through without transcoding and hope.
pass
def translate_headers(self, environ):
"""Translate CGI-environ header names to HTTP header names."""
for cgiName in environ:
# We assume all incoming header keys are uppercase already.
if cgiName in self.headerNames:
yield self.headerNames[cgiName], environ[cgiName]
elif cgiName[:5] == "HTTP_":
# Hackish attempt at recovering original header names.
translatedHeader = cgiName[5:].replace("_", "-")
yield translatedHeader, environ[cgiName]
class CPWSGIApp(object):
"""A WSGI application object for a CherryPy Application."""
pipeline = [
('ExceptionTrapper', ExceptionTrapper),
('InternalRedirector', InternalRedirector),
]
"""A list of (name, wsgiapp) pairs. Each 'wsgiapp' MUST be a
constructor that takes an initial, positional 'nextapp' argument,
plus optional keyword arguments, and returns a WSGI application
(that takes environ and start_response arguments). The 'name' can
be any you choose, and will correspond to keys in self.config."""
head = None
"""Rather than nest all apps in the pipeline on each call, it's only
done the first time, and the result is memoized into self.head. Set
this to None again if you change self.pipeline after calling self."""
config = {}
"""A dict whose keys match names listed in the pipeline. Each
value is a further dict which will be passed to the corresponding
named WSGI callable (from the pipeline) as keyword arguments."""
response_class = AppResponse
"""The class to instantiate and return as the next app in the WSGI chain.
"""
def __init__(self, cpapp, pipeline=None):
self.cpapp = cpapp
self.pipeline = self.pipeline[:]
if pipeline:
self.pipeline.extend(pipeline)
self.config = self.config.copy()
def tail(self, environ, start_response):
"""WSGI application callable for the actual CherryPy application.
You probably shouldn't call this; call self.__call__ instead,
so that any WSGI middleware in self.pipeline can run first.
"""
return self.response_class(environ, start_response, self.cpapp)
def __call__(self, environ, start_response):
head = self.head
if head is None:
# Create and nest the WSGI apps in our pipeline (in reverse order).
# Then memoize the result in self.head.
head = self.tail
for name, callable in self.pipeline[::-1]:
conf = self.config.get(name, {})
head = callable(head, **conf)
self.head = head
return head(environ, start_response)
def namespace_handler(self, k, v):
"""Config handler for the 'wsgi' namespace."""
if k == "pipeline":
# Note this allows multiple 'wsgi.pipeline' config entries
# (but each entry will be processed in a 'random' order).
# It should also allow developers to set default middleware
# in code (passed to self.__init__) that deployers can add to
# (but not remove) via config.
self.pipeline.extend(v)
elif k == "response_class":
self.response_class = v
else:
name, arg = k.split(".", 1)
bucket = self.config.setdefault(name, {})
bucket[arg] = v

70
deps/cherrypy/_cpwsgi_server.py vendored Normal file
View File

@@ -0,0 +1,70 @@
"""WSGI server interface (see PEP 333). This adds some CP-specific bits to
the framework-agnostic wsgiserver package.
"""
import sys
import cherrypy
from cherrypy import wsgiserver
class CPWSGIServer(wsgiserver.CherryPyWSGIServer):
"""Wrapper for wsgiserver.CherryPyWSGIServer.
wsgiserver has been designed to not reference CherryPy in any way,
so that it can be used in other frameworks and applications. Therefore,
we wrap it here, so we can set our own mount points from cherrypy.tree
and apply some attributes from config -> cherrypy.server -> wsgiserver.
"""
def __init__(self, server_adapter=cherrypy.server):
self.server_adapter = server_adapter
self.max_request_header_size = (
self.server_adapter.max_request_header_size or 0
)
self.max_request_body_size = (
self.server_adapter.max_request_body_size or 0
)
server_name = (self.server_adapter.socket_host or
self.server_adapter.socket_file or
None)
self.wsgi_version = self.server_adapter.wsgi_version
s = wsgiserver.CherryPyWSGIServer
s.__init__(self, server_adapter.bind_addr, cherrypy.tree,
self.server_adapter.thread_pool,
server_name,
max=self.server_adapter.thread_pool_max,
request_queue_size=self.server_adapter.socket_queue_size,
timeout=self.server_adapter.socket_timeout,
shutdown_timeout=self.server_adapter.shutdown_timeout,
accepted_queue_size=self.server_adapter.accepted_queue_size,
accepted_queue_timeout=self.server_adapter.accepted_queue_timeout,
)
self.protocol = self.server_adapter.protocol_version
self.nodelay = self.server_adapter.nodelay
if sys.version_info >= (3, 0):
ssl_module = self.server_adapter.ssl_module or 'builtin'
else:
ssl_module = self.server_adapter.ssl_module or 'pyopenssl'
if self.server_adapter.ssl_context:
adapter_class = wsgiserver.get_ssl_adapter_class(ssl_module)
self.ssl_adapter = adapter_class(
self.server_adapter.ssl_certificate,
self.server_adapter.ssl_private_key,
self.server_adapter.ssl_certificate_chain)
self.ssl_adapter.context = self.server_adapter.ssl_context
elif self.server_adapter.ssl_certificate:
adapter_class = wsgiserver.get_ssl_adapter_class(ssl_module)
self.ssl_adapter = adapter_class(
self.server_adapter.ssl_certificate,
self.server_adapter.ssl_private_key,
self.server_adapter.ssl_certificate_chain)
self.stats['Enabled'] = getattr(
self.server_adapter, 'statistics', False)
def error_log(self, msg="", level=20, traceback=False):
cherrypy.engine.log(msg, level, traceback)

298
deps/cherrypy/_helper.py vendored Normal file
View File

@@ -0,0 +1,298 @@
"""
Helper functions for CP apps
"""
import six
from cherrypy._cpcompat import urljoin as _urljoin, urlencode as _urlencode
from cherrypy._cpcompat import text_or_bytes
import cherrypy
def expose(func=None, alias=None):
"""
Expose the function or class, optionally providing an alias or set of aliases.
"""
def expose_(func):
func.exposed = True
if alias is not None:
if isinstance(alias, text_or_bytes):
parents[alias.replace(".", "_")] = func
else:
for a in alias:
parents[a.replace(".", "_")] = func
return func
import sys
import types
decoratable_types = types.FunctionType, types.MethodType, type,
if six.PY2:
# Old-style classes are type types.ClassType.
decoratable_types += types.ClassType,
if isinstance(func, decoratable_types):
if alias is None:
# @expose
func.exposed = True
return func
else:
# func = expose(func, alias)
parents = sys._getframe(1).f_locals
return expose_(func)
elif func is None:
if alias is None:
# @expose()
parents = sys._getframe(1).f_locals
return expose_
else:
# @expose(alias="alias") or
# @expose(alias=["alias1", "alias2"])
parents = sys._getframe(1).f_locals
return expose_
else:
# @expose("alias") or
# @expose(["alias1", "alias2"])
parents = sys._getframe(1).f_locals
alias = func
return expose_
def popargs(*args, **kwargs):
"""A decorator for _cp_dispatch
(cherrypy.dispatch.Dispatcher.dispatch_method_name).
Optional keyword argument: handler=(Object or Function)
Provides a _cp_dispatch function that pops off path segments into
cherrypy.request.params under the names specified. The dispatch
is then forwarded on to the next vpath element.
Note that any existing (and exposed) member function of the class that
popargs is applied to will override that value of the argument. For
instance, if you have a method named "list" on the class decorated with
popargs, then accessing "/list" will call that function instead of popping
it off as the requested parameter. This restriction applies to all
_cp_dispatch functions. The only way around this restriction is to create
a "blank class" whose only function is to provide _cp_dispatch.
If there are path elements after the arguments, or more arguments
are requested than are available in the vpath, then the 'handler'
keyword argument specifies the next object to handle the parameterized
request. If handler is not specified or is None, then self is used.
If handler is a function rather than an instance, then that function
will be called with the args specified and the return value from that
function used as the next object INSTEAD of adding the parameters to
cherrypy.request.args.
This decorator may be used in one of two ways:
As a class decorator:
@cherrypy.popargs('year', 'month', 'day')
class Blog:
def index(self, year=None, month=None, day=None):
#Process the parameters here; any url like
#/, /2009, /2009/12, or /2009/12/31
#will fill in the appropriate parameters.
def create(self):
#This link will still be available at /create. Defined functions
#take precedence over arguments.
Or as a member of a class:
class Blog:
_cp_dispatch = cherrypy.popargs('year', 'month', 'day')
#...
The handler argument may be used to mix arguments with built in functions.
For instance, the following setup allows different activities at the
day, month, and year level:
class DayHandler:
def index(self, year, month, day):
#Do something with this day; probably list entries
def delete(self, year, month, day):
#Delete all entries for this day
@cherrypy.popargs('day', handler=DayHandler())
class MonthHandler:
def index(self, year, month):
#Do something with this month; probably list entries
def delete(self, year, month):
#Delete all entries for this month
@cherrypy.popargs('month', handler=MonthHandler())
class YearHandler:
def index(self, year):
#Do something with this year
#...
@cherrypy.popargs('year', handler=YearHandler())
class Root:
def index(self):
#...
"""
# Since keyword arg comes after *args, we have to process it ourselves
# for lower versions of python.
handler = None
handler_call = False
for k, v in kwargs.items():
if k == 'handler':
handler = v
else:
raise TypeError(
"cherrypy.popargs() got an unexpected keyword argument '{0}'"
.format(k)
)
import inspect
if handler is not None \
and (hasattr(handler, '__call__') or inspect.isclass(handler)):
handler_call = True
def decorated(cls_or_self=None, vpath=None):
if inspect.isclass(cls_or_self):
# cherrypy.popargs is a class decorator
cls = cls_or_self
setattr(cls, cherrypy.dispatch.Dispatcher.dispatch_method_name, decorated)
return cls
# We're in the actual function
self = cls_or_self
parms = {}
for arg in args:
if not vpath:
break
parms[arg] = vpath.pop(0)
if handler is not None:
if handler_call:
return handler(**parms)
else:
cherrypy.request.params.update(parms)
return handler
cherrypy.request.params.update(parms)
# If we are the ultimate handler, then to prevent our _cp_dispatch
# from being called again, we will resolve remaining elements through
# getattr() directly.
if vpath:
return getattr(self, vpath.pop(0), None)
else:
return self
return decorated
def url(path="", qs="", script_name=None, base=None, relative=None):
"""Create an absolute URL for the given path.
If 'path' starts with a slash ('/'), this will return
(base + script_name + path + qs).
If it does not start with a slash, this returns
(base + script_name [+ request.path_info] + path + qs).
If script_name is None, cherrypy.request will be used
to find a script_name, if available.
If base is None, cherrypy.request.base will be used (if available).
Note that you can use cherrypy.tools.proxy to change this.
Finally, note that this function can be used to obtain an absolute URL
for the current request path (minus the querystring) by passing no args.
If you call url(qs=cherrypy.request.query_string), you should get the
original browser URL (assuming no internal redirections).
If relative is None or not provided, request.app.relative_urls will
be used (if available, else False). If False, the output will be an
absolute URL (including the scheme, host, vhost, and script_name).
If True, the output will instead be a URL that is relative to the
current request path, perhaps including '..' atoms. If relative is
the string 'server', the output will instead be a URL that is
relative to the server root; i.e., it will start with a slash.
"""
if isinstance(qs, (tuple, list, dict)):
qs = _urlencode(qs)
if qs:
qs = '?' + qs
if cherrypy.request.app:
if not path.startswith("/"):
# Append/remove trailing slash from path_info as needed
# (this is to support mistyped URL's without redirecting;
# if you want to redirect, use tools.trailing_slash).
pi = cherrypy.request.path_info
if cherrypy.request.is_index is True:
if not pi.endswith('/'):
pi = pi + '/'
elif cherrypy.request.is_index is False:
if pi.endswith('/') and pi != '/':
pi = pi[:-1]
if path == "":
path = pi
else:
path = _urljoin(pi, path)
if script_name is None:
script_name = cherrypy.request.script_name
if base is None:
base = cherrypy.request.base
newurl = base + script_name + path + qs
else:
# No request.app (we're being called outside a request).
# We'll have to guess the base from server.* attributes.
# This will produce very different results from the above
# if you're using vhosts or tools.proxy.
if base is None:
base = cherrypy.server.base()
path = (script_name or "") + path
newurl = base + path + qs
if './' in newurl:
# Normalize the URL by removing ./ and ../
atoms = []
for atom in newurl.split('/'):
if atom == '.':
pass
elif atom == '..':
atoms.pop()
else:
atoms.append(atom)
newurl = '/'.join(atoms)
# At this point, we should have a fully-qualified absolute URL.
if relative is None:
relative = getattr(cherrypy.request.app, "relative_urls", False)
# See http://www.ietf.org/rfc/rfc2396.txt
if relative == 'server':
# "A relative reference beginning with a single slash character is
# termed an absolute-path reference, as defined by <abs_path>..."
# This is also sometimes called "server-relative".
newurl = '/' + '/'.join(newurl.split('/', 3)[3:])
elif relative:
# "A relative reference that does not begin with a scheme name
# or a slash character is termed a relative-path reference."
old = url(relative=False).split('/')[:-1]
new = newurl.split('/')
while old and new:
a, b = old[0], new[0]
if a != b:
break
old.pop(0)
new.pop(0)
new = (['..'] * len(old)) + new
newurl = '/'.join(new)
return newurl

106
deps/cherrypy/daemon.py vendored Normal file
View File

@@ -0,0 +1,106 @@
"""The CherryPy daemon."""
import sys
import cherrypy
from cherrypy.process import plugins, servers
from cherrypy import Application
def start(configfiles=None, daemonize=False, environment=None,
fastcgi=False, scgi=False, pidfile=None, imports=None,
cgi=False):
"""Subscribe all engine plugins and start the engine."""
sys.path = [''] + sys.path
for i in imports or []:
exec("import %s" % i)
for c in configfiles or []:
cherrypy.config.update(c)
# If there's only one app mounted, merge config into it.
if len(cherrypy.tree.apps) == 1:
for app in cherrypy.tree.apps.values():
if isinstance(app, Application):
app.merge(c)
engine = cherrypy.engine
if environment is not None:
cherrypy.config.update({'environment': environment})
# Only daemonize if asked to.
if daemonize:
# Don't print anything to stdout/sterr.
cherrypy.config.update({'log.screen': False})
plugins.Daemonizer(engine).subscribe()
if pidfile:
plugins.PIDFile(engine, pidfile).subscribe()
if hasattr(engine, "signal_handler"):
engine.signal_handler.subscribe()
if hasattr(engine, "console_control_handler"):
engine.console_control_handler.subscribe()
if (fastcgi and (scgi or cgi)) or (scgi and cgi):
cherrypy.log.error("You may only specify one of the cgi, fastcgi, and "
"scgi options.", 'ENGINE')
sys.exit(1)
elif fastcgi or scgi or cgi:
# Turn off autoreload when using *cgi.
cherrypy.config.update({'engine.autoreload.on': False})
# Turn off the default HTTP server (which is subscribed by default).
cherrypy.server.unsubscribe()
addr = cherrypy.server.bind_addr
cls = (
servers.FlupFCGIServer if fastcgi else
servers.FlupSCGIServer if scgi else
servers.FlupCGIServer
)
f = cls(application=cherrypy.tree, bindAddress=addr)
s = servers.ServerAdapter(engine, httpserver=f, bind_addr=addr)
s.subscribe()
# Always start the engine; this will start all other services
try:
engine.start()
except:
# Assume the error has been logged already via bus.log.
sys.exit(1)
else:
engine.block()
def run():
from optparse import OptionParser
p = OptionParser()
p.add_option('-c', '--config', action="append", dest='config',
help="specify config file(s)")
p.add_option('-d', action="store_true", dest='daemonize',
help="run the server as a daemon")
p.add_option('-e', '--environment', dest='environment', default=None,
help="apply the given config environment")
p.add_option('-f', action="store_true", dest='fastcgi',
help="start a fastcgi server instead of the default HTTP "
"server")
p.add_option('-s', action="store_true", dest='scgi',
help="start a scgi server instead of the default HTTP server")
p.add_option('-x', action="store_true", dest='cgi',
help="start a cgi server instead of the default HTTP server")
p.add_option('-i', '--import', action="append", dest='imports',
help="specify modules to import")
p.add_option('-p', '--pidfile', dest='pidfile', default=None,
help="store the process id in the given file")
p.add_option('-P', '--Path', action="append", dest='Path',
help="add the given paths to sys.path")
options, args = p.parse_args()
if options.Path:
for p in options.Path:
sys.path.insert(0, p)
start(options.config, options.daemonize,
options.environment, options.fastcgi, options.scgi,
options.pidfile, options.imports, options.cgi)

Some files were not shown because too many files have changed in this diff Show More