core/script/gen_requirements_all.py

334 lines
8.4 KiB
Python
Raw Normal View History

2015-11-17 08:18:42 +00:00
#!/usr/bin/env python3
2016-03-09 10:15:04 +00:00
"""Generate an updated requirements_all.txt."""
2015-11-17 08:18:42 +00:00
import importlib
import os
import pkgutil
import re
2015-12-18 07:51:34 +00:00
import sys
import fnmatch
2015-11-17 08:18:42 +00:00
COMMENT_REQUIREMENTS = (
'RPi.GPIO',
'raspihats',
2016-05-12 05:56:05 +00:00
'rpi-rf',
'Adafruit_Python_DHT',
'Adafruit_BBIO',
2016-01-29 05:37:08 +00:00
'fritzconnection',
'pybluez',
'beacontools',
'bluepy',
'opencv-python',
'python-lirc',
'gattlib',
'pyuserinput',
'evdev',
'pycups',
2017-01-18 05:48:33 +00:00
'python-eq3bt',
'avion',
'decora',
'face_recognition',
'blinkt',
'smbus-cffi',
'envirophat',
2017-10-13 06:57:45 +00:00
'i2csense',
'credstash',
'bme680',
'homekit',
)
TEST_REQUIREMENTS = (
'aioautomatic',
'aiohttp_cors',
'aiohue',
'apns2',
'caldav',
'coinmarketcap',
'defusedxml',
'dsmr_parser',
'ephem',
'evohomeclient',
'feedparser',
'foobot_async',
'gTTS-token',
'HAP-python',
'ha-ffmpeg',
'haversine',
'hbmqtt',
'holidays',
'home-assistant-frontend',
Add HomematicIP Cloud Config Flow and Entries loading (#14861) * Add HomematicIP Cloud to config flow * Inititial trial for config_flow * Integrations text files * Load and write config_flow and init homematicip_cloud * Split into dedicated files * Ceanup of text messages * Working config_flow * Move imports inside a function * Enable laoding even no accesspoints are defined * Revert unnecassary changes in CONFIG_SCHEMA * Better error handling * fix flask8 * Migration to async for token generation * A few fixes * Simplify config_flow * Bump version to 9.6 with renamed package * Requirements file * First fixes after review * Implement async_step_import * Cleanup for Config Flow * First tests for homematicip_cloud setup * Remove config_flow tests * Really remove all things * Fix comment * Update picture * Add support for async_setup_entry to switch and climate platform * Update path of the config_flow picture * Refactoring for better tesability * Further tests implemented * Move 3th party lib inside function * Fix lint * Update requirments_test_all.txt file * UPdate of requirments_test_all.txt did not work * Furder cleanup in websocket connection * Remove a test for the hap * Revert "Remove a test for the hap" This reverts commit 968d58cba108e0f371022c7ab540374aa2ab13f4. * First tests implemented for config_flow * Fix lint * Rework of client registration process * Implemented tests for config_flow 100% coverage * Cleanup * Cleanup comments and code * Try to fix import problem * Add homematicip to the test env requirements
2018-07-06 21:05:34 +00:00
'homematicip',
'influxdb',
'libpurecoollink',
'libsoundtouch',
'mficlient',
'numpy',
'paho-mqtt',
'pexpect',
'pilight',
'pmsensor',
'prometheus_client',
'pushbullet.py',
'py-canary',
'pyblackbird',
'pydeconz',
'pydispatcher',
'PyJWT',
'pylitejet',
'pymonoprice',
'pynx584',
2018-04-08 19:59:19 +00:00
'pyqwikswitch',
'python-forecastio',
'python-nest',
'pytradfri\[async\]',
'pyunifi',
'pyupnp-async',
'pywebpush',
'restrictedpython',
'rflink',
'ring_doorbell',
'rxv',
'sleepyq',
'SoCo',
'somecomfort',
'sqlalchemy',
'statsd',
'uvcclient',
'voluptuous-serialize',
'warrant',
'yahoo-finance',
'pythonwhois',
Add platform and sensors for Vultr VPS (#9928) * Initial commit of Vultr components Have a working Vultr hub and binary sensor which pulls down the following attributes of your VPS: - Date created - Subscription id (server id) - Cost per month (in US$) - Operating System installed - IPv4 address - label (human readable name) - region - number of vcpus - which storage package chosen - IPV6 address (if applicable) - RAM amount Working next on sensor and then testing / coverage. * Added Vultr sensor for pending charges and current bandwidth. Refactored binary_sensor and hub too * Corrected is_on bases * Added basic tests for Vultr binary & platform * Updated require files * Changing test fixture to highlight different cases * Written basic test for sensor.vultr * Resolved linting errors and broken test * Increase test coverage and corrected docs * Resolved hound issues * Revert back negative binary test * Another hound resolve * Refactoring and adding is switch, moving over to vultr branch * Made Vultr components more resiliant to invalid configs * Added negetive test for vultr binary sensor * Added better testing of vultr sensor * Resolved vultr platform test affecting subsequent vultr tests * Moving VULTR components to single use design * Added in sensor name config * Added missing sensors var * Resolved init data setting of sensors, added in name conf to switch * Made the Vultr component more resiliant to startup failure with better alerting * Various Vultr component changes - Refactored sensor, binary_sensor, and switch to reference one subscription - Renamed CURRENT_BANDWIDTH_GB monitored condition to CURRENT_BANDWIDTH_USED - Improved test coverage * Resolved local tox linting issue * Added more testing for Vultr switch * Improved test coverage for Vultr components * Made PR comment changes to vultr binary sensor * Made PR comment changes to Vultr sensor * resolved PR comments for Vultr Switch * Resolved vultr sensor name and improved tests * Improved Vultr switch testing (default name formatting) * Removed vultr hub failure checking
2017-11-05 13:10:14 +00:00
'wakeonlan',
'vultr'
)
IGNORE_PACKAGES = (
'homeassistant.components.recorder.models',
'homeassistant.components.homekit.*'
)
IGNORE_PIN = ('colorlog>2.1,<3', 'keyring>=9.3,<10.0', 'urllib3')
IGNORE_REQ = (
'colorama<=1', # Windows only requirement in check_config
)
2017-01-22 16:34:00 +00:00
URL_PIN = ('https://home-assistant.io/developers/code_review_platform/'
'#1-requirements')
2015-11-17 08:18:42 +00:00
CONSTRAINT_PATH = os.path.join(os.path.dirname(__file__),
'../homeassistant/package_constraints.txt')
CONSTRAINT_BASE = """
2018-02-20 07:10:44 +00:00
# Breaks Python 3.6 and is not needed for our supported Python versions
enum34==1000000000.0.0
# This is a old unmaintained library and is replaced with pycryptodome
pycrypto==1000000000.0.0
"""
2015-11-17 08:18:42 +00:00
def explore_module(package, explore_children):
2016-03-09 10:15:04 +00:00
"""Explore the modules."""
2015-11-17 08:18:42 +00:00
module = importlib.import_module(package)
found = []
if not hasattr(module, '__path__'):
return found
for _, name, _ in pkgutil.iter_modules(module.__path__, package + '.'):
2015-11-17 08:18:42 +00:00
found.append(name)
if explore_children:
found.extend(explore_module(name, False))
return found
def core_requirements():
2016-03-09 10:15:04 +00:00
"""Gather core requirements out of setup.py."""
2015-11-17 08:18:42 +00:00
with open('setup.py') as inp:
reqs_raw = re.search(
r'REQUIRES = \[(.*?)\]', inp.read(), re.S).group(1)
return re.findall(r"'(.*?)'", reqs_raw)
def comment_requirement(req):
2016-03-09 10:15:04 +00:00
"""Some requirements don't install on all systems."""
return any(ign in req for ign in COMMENT_REQUIREMENTS)
2015-11-25 22:31:04 +00:00
def gather_modules():
"""Collect the information."""
2016-02-01 07:52:42 +00:00
reqs = {}
2015-11-17 08:18:42 +00:00
errors = []
2015-11-25 22:31:04 +00:00
for package in sorted(explore_module('homeassistant.components', True) +
explore_module('homeassistant.scripts', True)):
2015-11-17 08:18:42 +00:00
try:
module = importlib.import_module(package)
except ImportError:
for pattern in IGNORE_PACKAGES:
if fnmatch.fnmatch(package, pattern):
break
else:
errors.append(package)
2015-11-17 08:18:42 +00:00
continue
if not getattr(module, 'REQUIREMENTS', None):
continue
for req in module.REQUIREMENTS:
if req in IGNORE_REQ:
continue
if req.partition('==')[1] == '' and req not in IGNORE_PIN:
2017-01-22 16:34:00 +00:00
errors.append(
"{}[Please pin requirement {}, see {}]".format(
package, req, URL_PIN))
2015-11-17 08:18:42 +00:00
reqs.setdefault(req, []).append(package)
2016-02-01 07:52:42 +00:00
for key in reqs:
reqs[key] = sorted(reqs[key],
key=lambda name: (len(name.split('.')), name))
2015-11-17 08:18:42 +00:00
if errors:
2015-12-18 07:51:34 +00:00
print("******* ERROR")
print("Errors while importing: ", ', '.join(errors))
print("Make sure you import 3rd party libraries inside methods.")
return None
2015-11-17 08:18:42 +00:00
return reqs
def generate_requirements_list(reqs):
"""Generate a pip file based on requirements."""
output = []
2016-02-01 07:52:42 +00:00
for pkg, requirements in sorted(reqs.items(), key=lambda item: item[0]):
2015-11-17 08:18:42 +00:00
for req in sorted(requirements,
key=lambda name: (len(name.split('.')), name)):
2015-11-25 22:31:04 +00:00
output.append('\n# {}'.format(req))
if comment_requirement(pkg):
2015-11-25 22:31:04 +00:00
output.append('\n# {}\n'.format(pkg))
else:
2015-11-25 22:31:04 +00:00
output.append('\n{}\n'.format(pkg))
return ''.join(output)
def requirements_all_output(reqs):
"""Generate output for requirements_all."""
output = []
output.append('# Home Assistant core')
output.append('\n')
output.append('\n'.join(core_requirements()))
output.append('\n')
output.append(generate_requirements_list(reqs))
return ''.join(output)
def requirements_test_output(reqs):
"""Generate output for test_requirements."""
output = []
output.append('# Home Assistant test')
output.append('\n')
with open('requirements_test.txt') as test_file:
output.append(test_file.read())
output.append('\n')
filtered = {key: value for key, value in reqs.items()
if any(
re.search(r'(^|#){}($|[=><])'.format(ign),
key) is not None for ign in TEST_REQUIREMENTS)}
output.append(generate_requirements_list(filtered))
2015-11-25 22:31:04 +00:00
return ''.join(output)
def gather_constraints():
"""Construct output for constraint file."""
return '\n'.join(core_requirements() + [''])
def write_requirements_file(data):
2016-03-09 10:15:04 +00:00
"""Write the modules to the requirements_all.txt."""
with open('requirements_all.txt', 'w+', newline="\n") as req_file:
2015-11-25 22:31:04 +00:00
req_file.write(data)
def write_test_requirements_file(data):
"""Write the modules to the requirements_all.txt."""
with open('requirements_test_all.txt', 'w+', newline="\n") as req_file:
req_file.write(data)
def write_constraints_file(data):
"""Write constraints to a file."""
with open(CONSTRAINT_PATH, 'w+', newline="\n") as req_file:
req_file.write(data + CONSTRAINT_BASE)
def validate_requirements_file(data):
2016-03-09 10:15:04 +00:00
"""Validate if requirements_all.txt is up to date."""
2015-12-18 07:51:34 +00:00
with open('requirements_all.txt', 'r') as req_file:
return data == req_file.read()
2015-12-18 07:51:34 +00:00
def validate_requirements_test_file(data):
"""Validate if requirements_all.txt is up to date."""
with open('requirements_test_all.txt', 'r') as req_file:
return data == req_file.read()
def validate_constraints_file(data):
"""Validate if constraints is up to date."""
with open(CONSTRAINT_PATH, 'r') as req_file:
return data + CONSTRAINT_BASE == req_file.read()
2018-03-09 20:27:39 +00:00
def main(validate):
2016-03-09 10:15:04 +00:00
"""Main section of the script."""
2015-11-25 22:31:04 +00:00
if not os.path.isfile('requirements_all.txt'):
print('Run this from HA root dir')
2018-03-09 20:27:39 +00:00
return 1
2015-11-25 22:31:04 +00:00
data = gather_modules()
if data is None:
2018-03-09 20:27:39 +00:00
return 1
2015-12-18 07:51:34 +00:00
constraints = gather_constraints()
reqs_file = requirements_all_output(data)
reqs_test_file = requirements_test_output(data)
2018-03-09 20:27:39 +00:00
if validate:
errors = []
if not validate_requirements_file(reqs_file):
errors.append("requirements_all.txt is not up to date")
if not validate_requirements_test_file(reqs_test_file):
errors.append("requirements_test_all.txt is not up to date")
if not validate_constraints_file(constraints):
errors.append(
"home-assistant/package_constraints.txt is not up to date")
if errors:
print("******* ERROR")
print('\n'.join(errors))
print("Please run script/gen_requirements_all.py")
2018-03-09 20:27:39 +00:00
return 1
2018-03-09 20:27:39 +00:00
return 0
write_requirements_file(reqs_file)
write_test_requirements_file(reqs_test_file)
write_constraints_file(constraints)
2018-03-09 20:27:39 +00:00
return 0
2015-11-17 08:18:42 +00:00
2016-11-19 05:47:59 +00:00
2015-11-17 08:18:42 +00:00
if __name__ == '__main__':
2018-03-09 20:27:39 +00:00
_VAL = sys.argv[-1] == 'validate'
sys.exit(main(_VAL))