Generate requirements_* from manifests (#22718)

## Description:
Generate requirements_* from manifests (if present). If not, fallback to the current approach of reading `REQUIREMENTS` from the module attribute. I disabled exploring the children of the `homeassistant.components.*` packages since that will just add a dependency (from the manifest) due to each of the python files in the package. Just having one for the top level package should be sufficient.

**Related issue (if applicable):** relates to #22700 

## Checklist:
  - [x] The code change is tested and works locally.
  - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass**
  - [x] There is no commented out code in this PR.

[ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L14
[ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L23


Co-authored-by: Jason Hu <awaregit@gmail.com>
This commit is contained in:
Rohan Kapoor 2019-04-04 21:29:29 -07:00 committed by Robbie Trencheny
parent 6c5f0b7434
commit d15eedc0fb
4 changed files with 515 additions and 524 deletions

File diff suppressed because it is too large Load diff

View file

@ -26,19 +26,19 @@ HAP-python==2.4.2
# homeassistant.components.owntracks
PyNaCl==1.3.0
# homeassistant.components.rmvtransport.sensor
# homeassistant.components.rmvtransport
PyRMVtransport==0.1.3
# homeassistant.components.transport_nsw.sensor
# homeassistant.components.transport_nsw
PyTransportNSW==0.1.1
# homeassistant.components.yessssms.notify
# homeassistant.components.yessssms
YesssSMS==0.2.3
# homeassistant.components.ambient_station
aioambient==0.2.0
aioambient==0.1.3
# homeassistant.components.automatic.device_tracker
# homeassistant.components.automatic
aioautomatic==0.6.5
# homeassistant.components.aws
@ -54,7 +54,7 @@ aiohue==1.9.1
# homeassistant.components.unifi
aiounifi==4
# homeassistant.components.apns.notify
# homeassistant.components.apns
apns2==0.3.0
# homeassistant.components.stream
@ -66,49 +66,49 @@ axis==19
# homeassistant.components.zha
bellows-homeassistant==0.7.2
# homeassistant.components.caldav.calendar
# homeassistant.components.caldav
caldav==0.5.0
# homeassistant.components.coinmarketcap.sensor
# homeassistant.components.coinmarketcap
coinmarketcap==5.0.3
# homeassistant.components.ihc
# homeassistant.components.namecheapdns
# homeassistant.components.ohmconnect.sensor
# homeassistant.components.upc_connect.device_tracker
# homeassistant.components.ohmconnect
# homeassistant.components.upc_connect
defusedxml==0.5.0
# homeassistant.components.dsmr.sensor
# homeassistant.components.dsmr
dsmr_parser==0.12
# homeassistant.components.ee_brightbox.device_tracker
# homeassistant.components.ee_brightbox
eebrightbox==0.0.4
# homeassistant.components.emulated_roku
emulated_roku==0.1.8
# homeassistant.components.season.sensor
# homeassistant.components.season
ephem==3.7.6.0
# homeassistant.components.evohome
# homeassistant.components.honeywell.climate
# homeassistant.components.honeywell
evohomeclient==0.3.2
# homeassistant.components.feedreader
feedparser-homeassistant==5.2.2.dev1
# homeassistant.components.foobot.sensor
# homeassistant.components.foobot
foobot_async==0.3.1
# homeassistant.components.google.tts
# homeassistant.components.google
gTTS-token==1.1.3
# homeassistant.components.geo_json_events.geo_location
# homeassistant.components.nsw_rural_fire_service_feed.geo_location
# homeassistant.components.usgs_earthquakes_feed.geo_location
# homeassistant.components.geo_json_events
# homeassistant.components.nsw_rural_fire_service_feed
# homeassistant.components.usgs_earthquakes_feed
geojson_client==0.3
# homeassistant.components.geo_rss_events.sensor
# homeassistant.components.geo_rss_events
georss_generic_client==0.2
# homeassistant.components.ffmpeg
@ -120,13 +120,13 @@ hangups==0.4.6
# homeassistant.components.cloud
hass-nabucasa==0.11
# homeassistant.components.mqtt.server
# homeassistant.components.mqtt
hbmqtt==0.9.4
# homeassistant.components.jewish_calendar.sensor
# homeassistant.components.jewish_calendar
hdate==0.8.7
# homeassistant.components.workday.binary_sensor
# homeassistant.components.workday
holidays==0.9.10
# homeassistant.components.frontend
@ -139,7 +139,6 @@ homekit[IP]==0.13.0
homematicip==0.10.6
# homeassistant.components.influxdb
# homeassistant.components.influxdb.sensor
influxdb==5.2.0
# homeassistant.components.verisure
@ -148,7 +147,7 @@ jsonpath==0.75
# homeassistant.components.dyson
libpurecool==0.5.0
# homeassistant.components.soundtouch.media_player
# homeassistant.components.soundtouch
libsoundtouch==0.7.2
# homeassistant.components.luftdaten
@ -157,38 +156,36 @@ luftdaten==0.3.4
# homeassistant.components.mythicbeastsdns
mbddns==0.1.2
# homeassistant.components.mfi.sensor
# homeassistant.components.mfi.switch
# homeassistant.components.mfi
mficlient==0.3.0
# homeassistant.components.opencv.image_processing
# homeassistant.components.pollen.sensor
# homeassistant.components.tensorflow.image_processing
# homeassistant.components.trend.binary_sensor
# homeassistant.components.opencv
# homeassistant.components.pollen
# homeassistant.components.tensorflow
# homeassistant.components.trend
numpy==1.16.2
# homeassistant.components.mqtt
# homeassistant.components.shiftr
paho-mqtt==1.4.0
# homeassistant.components.aruba.device_tracker
# homeassistant.components.cisco_ios.device_tracker
# homeassistant.components.pandora.media_player
# homeassistant.components.unifi_direct.device_tracker
# homeassistant.components.aruba
# homeassistant.components.cisco_ios
# homeassistant.components.pandora
# homeassistant.components.unifi_direct
pexpect==4.6.0
# homeassistant.components.pilight
pilight==0.1.1
# homeassistant.components.mhz19.sensor
# homeassistant.components.serial_pm.sensor
# homeassistant.components.mhz19
# homeassistant.components.serial_pm
pmsensor==0.4
# homeassistant.components.prometheus
prometheus_client==0.2.0
# homeassistant.components.pushbullet.notify
# homeassistant.components.pushbullet.sensor
# homeassistant.components.pushbullet
pushbullet.py==0.11.0
# homeassistant.components.canary
@ -197,7 +194,7 @@ py-canary==0.5.0
# homeassistant.components.tplink
pyHS100==0.3.4
# homeassistant.components.blackbird.media_player
# homeassistant.components.blackbird
pyblackbird==0.5
# homeassistant.components.deconz
@ -215,11 +212,10 @@ pyhomematic==0.1.58
# homeassistant.components.litejet
pylitejet==0.1
# homeassistant.components.monoprice.media_player
# homeassistant.components.monoprice
pymonoprice==0.3
# homeassistant.components.nx584.alarm_control_panel
# homeassistant.components.nx584.binary_sensor
# homeassistant.components.nx584
pynx584==0.4
# homeassistant.components.openuv
@ -227,7 +223,7 @@ pyopenuv==1.0.9
# homeassistant.auth.mfa_modules.notify
# homeassistant.auth.mfa_modules.totp
# homeassistant.components.otp.sensor
# homeassistant.components.otp
pyotp==2.2.6
# homeassistant.components.ps4
@ -248,24 +244,23 @@ pysonos==0.0.8
# homeassistant.components.spc
pyspcwebgw==0.4.0
# homeassistant.components.darksky.sensor
# homeassistant.components.darksky.weather
# homeassistant.components.darksky
python-forecastio==1.4.0
# homeassistant.components.nest
python-nest==4.1.0
# homeassistant.components.awair.sensor
# homeassistant.components.awair
python_awair==0.0.3
# homeassistant.components.tradfri
pytradfri[async]==6.0.1
# homeassistant.components.unifi.device_tracker
# homeassistant.components.unifi
pyunifi==2.16
# homeassistant.components.html5.notify
pywebpush==1.9.2
# homeassistant.components.html5
pywebpush==1.6.0
# homeassistant.components.rainmachine
regenmaschine==1.4.0
@ -279,7 +274,7 @@ rflink==0.0.37
# homeassistant.components.ring
ring_doorbell==0.2.3
# homeassistant.components.yamaha.media_player
# homeassistant.components.yamaha
rxv==0.6.0
# homeassistant.components.simplisafe
@ -291,14 +286,14 @@ sleepyq==0.6
# homeassistant.components.smhi
smhi-pkg==1.0.10
# homeassistant.components.honeywell.climate
# homeassistant.components.honeywell
somecomfort==0.5.2
# homeassistant.components.recorder
# homeassistant.components.sql.sensor
# homeassistant.components.sql
sqlalchemy==1.3.0
# homeassistant.components.srp_energy.sensor
# homeassistant.components.srp_energy
srpenergy==1.0.6
# homeassistant.components.statsd
@ -307,7 +302,7 @@ statsd==3.2.1
# homeassistant.components.toon
toonapilib==3.2.2
# homeassistant.components.uvc.camera
# homeassistant.components.uvc
uvcclient==0.11.0
# homeassistant.components.verisure
@ -316,10 +311,9 @@ vsure==1.5.2
# homeassistant.components.vultr
vultr==0.1.2
# homeassistant.components.panasonic_viera
# homeassistant.components.samsungtv
# homeassistant.components.wake_on_lan
# homeassistant.components.panasonic_viera.media_player
# homeassistant.components.samsungtv.media_player
# homeassistant.components.wake_on_lan.switch
wakeonlan==1.1.6
# homeassistant.components.zha

View file

@ -7,6 +7,8 @@ import pkgutil
import re
import sys
from script.manifest.requirements import gather_requirements_from_manifests
COMMENT_REQUIREMENTS = (
'Adafruit-DHT',
'Adafruit_BBIO',
@ -213,36 +215,8 @@ def gather_modules():
errors = []
for package in sorted(
explore_module('homeassistant.components', True) +
explore_module('homeassistant.scripts', True) +
explore_module('homeassistant.auth', True)):
try:
module = importlib.import_module(package)
except ImportError as err:
for pattern in IGNORE_PACKAGES:
if fnmatch.fnmatch(package, pattern):
break
else:
print("{}: {}".format(package.replace('.', '/') + '.py', err))
errors.append(package)
continue
if not getattr(module, 'REQUIREMENTS', None):
continue
for req in module.REQUIREMENTS:
if req in IGNORE_REQ:
continue
if '://' in req and 'pyharmony' not in req:
errors.append(
"{}[Only pypi dependencies are allowed: {}]".format(
package, req))
if req.partition('==')[1] == '' and req not in IGNORE_PIN:
errors.append(
"{}[Please pin requirement {}, see {}]".format(
package, req, URL_PIN))
reqs.setdefault(req, []).append(package)
gather_requirements_from_manifests(process_requirements, errors, reqs)
gather_requirements_from_modules(errors, reqs)
for key in reqs:
reqs[key] = sorted(reqs[key],
@ -257,12 +231,47 @@ def gather_modules():
return reqs
def gather_requirements_from_modules(errors, reqs):
"""Collect the requirements from the modules directly."""
for package in sorted(
explore_module('homeassistant.scripts', True) +
explore_module('homeassistant.auth', True)):
try:
module = importlib.import_module(package)
except ImportError as err:
for pattern in IGNORE_PACKAGES:
if fnmatch.fnmatch(package, pattern):
break
else:
print("{}: {}".format(package.replace('.', '/') + '.py', err))
errors.append(package)
continue
if getattr(module, 'REQUIREMENTS', None):
process_requirements(errors, module.REQUIREMENTS, package, reqs)
def process_requirements(errors, module_requirements, package, reqs):
"""Process all of the requirements."""
for req in module_requirements:
if req in IGNORE_REQ:
continue
if '://' in req:
errors.append(
"{}[Only pypi dependencies are allowed: {}]".format(
package, req))
if req.partition('==')[1] == '' and req not in IGNORE_PIN:
errors.append(
"{}[Please pin requirement {}, see {}]".format(
package, req, URL_PIN))
reqs.setdefault(req, []).append(package)
def generate_requirements_list(reqs):
"""Generate a pip file based on requirements."""
output = []
for pkg, requirements in sorted(reqs.items(), key=lambda item: item[0]):
for req in sorted(requirements,
key=lambda name: (len(name.split('.')), name)):
for req in sorted(requirements):
output.append('\n# {}'.format(req))
if comment_requirement(pkg):

View file

@ -0,0 +1,22 @@
"""Helpers to gather requirements from manifests."""
from .manifest_helper import iter_manifests
def gather_requirements_from_manifests(process_requirements, errors, reqs):
"""Gather all of the requirements from manifests."""
for manifest in iter_manifests():
assert manifest['domain']
if manifest.get('requirements') is None:
errors.append(
'The manifest for component {} is invalid. Please run'
'script/manifest/validate.py'.format(manifest['domain'])
)
continue
process_requirements(
errors,
manifest['requirements'],
'homeassistant.components.{}'.format(manifest['domain']),
reqs
)