diff --git a/.travis.yml b/.travis.yml index f928b372a0..54015c0556 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,20 +1,30 @@ sudo: false language: python -python: - - "2.7" -jobs: + +matrix: + fast_finish: true include: - - name: "Lint" - install: - - pip install -r requirements.txt - - pip install flake8==3.5.0 pylint==1.9.3 tzlocal pillow + - python: "2.7" + env: TARGET=Lint2.7 + install: pip install -e . && pip install flake8==3.6.0 pylint==1.9.4 tzlocal pillow script: - flake8 esphomeyaml - pylint esphomeyaml - - name: "Test" - install: - - pip install -e . - - pip install tzlocal pillow + - python: "3.5.3" + env: TARGET=Lint3.5 + install: pip install -U https://github.com/platformio/platformio-core/archive/develop.zip && pip install -e . && pip install flake8==3.6.0 pylint==2.2.2 tzlocal pillow + script: + - flake8 esphomeyaml + - pylint esphomeyaml + - python: "2.7" + env: TARGET=Test2.7 + install: pip install -e . && pip install flake8==3.6.0 pylint==1.9.4 tzlocal pillow + script: + - esphomeyaml tests/test1.yaml compile + - esphomeyaml tests/test2.yaml compile + - python: "3.5.3" + env: TARGET=Test3.5 + install: pip install -U https://github.com/platformio/platformio-core/archive/develop.zip && pip install -e . && pip install flake8==3.6.0 pylint==2.2.2 tzlocal pillow script: - esphomeyaml tests/test1.yaml compile - esphomeyaml tests/test2.yaml compile diff --git a/docker/Dockerfile.lint b/docker/Dockerfile.lint index dc87801b53..7855aadc8c 100644 --- a/docker/Dockerfile.lint +++ b/docker/Dockerfile.lint @@ -3,4 +3,4 @@ FROM python:2.7 COPY requirements.txt /requirements.txt RUN pip install -r /requirements.txt && \ - pip install flake8==3.5.0 pylint==1.9.3 tzlocal pillow + pip install flake8==3.6.0 pylint==1.9.4 tzlocal pillow diff --git a/esphomeyaml/__main__.py b/esphomeyaml/__main__.py index c7fb212341..dfe3c58ea5 100644 --- a/esphomeyaml/__main__.py +++ b/esphomeyaml/__main__.py @@ -16,8 +16,9 @@ from esphomeyaml.const import CONF_BAUD_RATE, CONF_ESPHOMEYAML, CONF_LOGGER, CON from esphomeyaml.core import CORE, EsphomeyamlError from esphomeyaml.cpp_generator import Expression, RawStatement, add, statement from esphomeyaml.helpers import color, indent -from esphomeyaml.storage_json import StorageJSON, storage_path, start_update_check_thread, \ - esphomeyaml_storage_path +from esphomeyaml.py_compat import safe_input, text_type +from esphomeyaml.storage_json import StorageJSON, esphomeyaml_storage_path, \ + start_update_check_thread, storage_path from esphomeyaml.util import run_external_command, safe_print _LOGGER = logging.getLogger(__name__) @@ -50,7 +51,7 @@ def choose_prompt(options): safe_print(u" [{}] {}".format(i + 1, desc)) while True: - opt = raw_input('(number): ') + opt = safe_input('(number): ') if opt in options: opt = options.index(opt) break @@ -140,7 +141,7 @@ def write_cpp(config): if not config[CONF_ESPHOMEYAML][CONF_USE_CUSTOM_CODE]: if isinstance(exp, Expression) and not exp.required: continue - all_code.append(unicode(statement(exp))) + all_code.append(text_type(statement(exp))) writer.write_platformio_project() @@ -199,7 +200,7 @@ def upload_program(config, args, host): if storage is not None and not storage.use_legacy_ota: return res - _LOGGER.warn("OTA v2 method failed. Trying with legacy OTA...") + _LOGGER.warning("OTA v2 method failed. Trying with legacy OTA...") return espota2.run_legacy_ota(verbose, host_port, host, remote_port, password, CORE.firmware_bin) @@ -208,9 +209,9 @@ def show_logs(config, args, port): if get_port_type(port) == 'SERIAL': run_miniterm(config, port) return 0 - elif get_port_type(port) == 'NETWORK': + if get_port_type(port) == 'NETWORK': return run_logs(config, port) - elif get_port_type(port) == 'MQTT': + if get_port_type(port) == 'MQTT': return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id) raise ValueError diff --git a/esphomeyaml/api/client.py b/esphomeyaml/api/client.py index da5b71b219..2b2987afde 100644 --- a/esphomeyaml/api/client.py +++ b/esphomeyaml/api/client.py @@ -14,6 +14,7 @@ import esphomeyaml.api.api_pb2 as pb from esphomeyaml.const import CONF_PASSWORD, CONF_PORT from esphomeyaml.core import EsphomeyamlError from esphomeyaml.helpers import resolve_ip_address, indent, color +from esphomeyaml.py_compat import text_type from esphomeyaml.util import safe_print _LOGGER = logging.getLogger(__name__) @@ -254,14 +255,14 @@ class APIClient(threading.Thread): def _send_message(self, msg): # type: (message.Message) -> None - for message_type, klass in MESSAGE_TYPE_TO_PROTO.iteritems(): + for message_type, klass in MESSAGE_TYPE_TO_PROTO.items(): if isinstance(msg, klass): break else: raise ValueError encoded = msg.SerializeToString() - _LOGGER.debug("Sending %s:\n%s", type(msg), indent(unicode(msg))) + _LOGGER.debug("Sending %s:\n%s", type(msg), indent(text_type(msg))) req = chr(0x00) req += _varuint_to_bytes(len(encoded)) req += _varuint_to_bytes(message_type) @@ -435,12 +436,14 @@ def run_logs(config, address): while retry_timer: retry_timer.pop(0).cancel() + error = None try: cli.connect() cli.login() - except APIConnectionError as error: - pass - else: + except APIConnectionError as err: # noqa + error = err + + if error is None: _LOGGER.info("Successfully connected to %s", address) return diff --git a/esphomeyaml/automation.py b/esphomeyaml/automation.py index 9e0539fc62..a3cb8e3a57 100644 --- a/esphomeyaml/automation.py +++ b/esphomeyaml/automation.py @@ -41,7 +41,7 @@ def validate_recursive_condition(value): raise vol.Invalid(u"Unable to find condition with the name '{}', is the " u"component loaded?".format(key), path + [key]) item.setdefault(CONF_CONDITION_ID, None) - key2 = next((x for x in item if x != CONF_CONDITION_ID and x != key), None) + key2 = next((x for x in item if x not in (CONF_CONDITION_ID, key)), None) if key2 is not None: raise vol.Invalid(u"Cannot have two conditions in one item. Key '{}' overrides '{}'! " u"Did you forget to indent the block inside the condition?" @@ -76,7 +76,7 @@ def validate_recursive_action(value): raise vol.Invalid(u"Unable to find action with the name '{}', is the component loaded?" u"".format(key), path + [key]) item.setdefault(CONF_ACTION_ID, None) - key2 = next((x for x in item if x != CONF_ACTION_ID and x != key), None) + key2 = next((x for x in item if x not in (CONF_ACTION_ID, key)), None) if key2 is not None: raise vol.Invalid(u"Cannot have two actions in one item. Key '{}' overrides '{}'! " u"Did you forget to indent the block inside the action?" diff --git a/esphomeyaml/components/api.py b/esphomeyaml/components/api.py index 32d6b2f4b1..c1ed16ae95 100644 --- a/esphomeyaml/components/api.py +++ b/esphomeyaml/components/api.py @@ -43,7 +43,7 @@ BUILD_FLAGS = '-DUSE_API' def lib_deps(config): if CORE.is_esp32: return 'AsyncTCP@1.0.1' - elif CORE.is_esp8266: + if CORE.is_esp8266: return 'ESPAsyncTCP@1.1.3' raise NotImplementedError diff --git a/esphomeyaml/components/binary_sensor/__init__.py b/esphomeyaml/components/binary_sensor/__init__.py index a79a396c50..2a3c909c20 100644 --- a/esphomeyaml/components/binary_sensor/__init__.py +++ b/esphomeyaml/components/binary_sensor/__init__.py @@ -14,6 +14,7 @@ from esphomeyaml.core import CORE from esphomeyaml.cpp_generator import process_lambda, ArrayInitializer, add, Pvariable, \ StructInitializer, get_variable from esphomeyaml.cpp_types import esphomelib_ns, Nameable, Trigger, NoArg, Component, App, bool_ +from esphomeyaml.py_compat import string_types DEVICE_CLASSES = [ '', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas', @@ -70,7 +71,7 @@ MULTI_CLICK_TIMING_SCHEMA = vol.Schema({ def parse_multi_click_timing_str(value): - if not isinstance(value, basestring): + if not isinstance(value, string_types): return value parts = value.lower().split(' ') diff --git a/esphomeyaml/components/binary_sensor/remote_receiver.py b/esphomeyaml/components/binary_sensor/remote_receiver.py index 5a3178c052..f189a2225b 100644 --- a/esphomeyaml/components/binary_sensor/remote_receiver.py +++ b/esphomeyaml/components/binary_sensor/remote_receiver.py @@ -73,40 +73,40 @@ def receiver_base(full_config): key, config = next((k, v) for k, v in full_config.items() if k in REMOTE_KEYS) if key == CONF_LG: return LGReceiver.new(name, config[CONF_DATA], config[CONF_NBITS]) - elif key == CONF_NEC: + if key == CONF_NEC: return NECReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND]) - elif key == CONF_PANASONIC: + if key == CONF_PANASONIC: return PanasonicReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND]) - elif key == CONF_SAMSUNG: + if key == CONF_SAMSUNG: return SamsungReceiver.new(name, config[CONF_DATA]) - elif key == CONF_SONY: + if key == CONF_SONY: return SonyReceiver.new(name, config[CONF_DATA], config[CONF_NBITS]) - elif key == CONF_RAW: + if key == CONF_RAW: data = ArrayInitializer(*config, multiline=False) return RawReceiver.new(name, data) - elif key == CONF_RC_SWITCH_RAW: + if key == CONF_RC_SWITCH_RAW: return RCSwitchRawReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), binary_code(config[CONF_CODE]), len(config[CONF_CODE])) - elif key == CONF_RC_SWITCH_TYPE_A: + if key == CONF_RC_SWITCH_TYPE_A: return RCSwitchTypeAReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), binary_code(config[CONF_GROUP]), binary_code(config[CONF_DEVICE]), config[CONF_STATE]) - elif key == CONF_RC_SWITCH_TYPE_B: + if key == CONF_RC_SWITCH_TYPE_B: return RCSwitchTypeBReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), config[CONF_ADDRESS], config[CONF_CHANNEL], config[CONF_STATE]) - elif key == CONF_RC_SWITCH_TYPE_C: + if key == CONF_RC_SWITCH_TYPE_C: return RCSwitchTypeCReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), ord(config[CONF_FAMILY][0]) - ord('a'), config[CONF_GROUP], config[CONF_DEVICE], config[CONF_STATE]) - elif key == CONF_RC_SWITCH_TYPE_D: + if key == CONF_RC_SWITCH_TYPE_D: return RCSwitchTypeDReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), ord(config[CONF_GROUP][0]) - ord('a'), config[CONF_DEVICE], config[CONF_STATE]) - else: - raise NotImplementedError("Unknown receiver type {}".format(config)) + + raise NotImplementedError("Unknown receiver type {}".format(config)) def to_code(config): diff --git a/esphomeyaml/components/font.py b/esphomeyaml/components/font.py index 10cb91efac..ab1611231e 100644 --- a/esphomeyaml/components/font.py +++ b/esphomeyaml/components/font.py @@ -33,10 +33,9 @@ def validate_glyphs(value): if len(x_) < len(y_): return -1 - elif len(x_) > len(y_): + if len(x_) > len(y_): return 1 - else: - raise vol.Invalid(u"Found duplicate glyph {}".format(x)) + raise vol.Invalid(u"Found duplicate glyph {}".format(x)) value.sort(cmp=comparator) return value @@ -47,11 +46,11 @@ def validate_pillow_installed(value): import PIL except ImportError: raise vol.Invalid("Please install the pillow python package to use this feature. " - "(pip2 install pillow)") + "(pip install pillow)") if PIL.__version__[0] < '4': raise vol.Invalid("Please update your pillow installation to at least 4.0.x. " - "(pip2 install -U pillow)") + "(pip install -U pillow)") return value diff --git a/esphomeyaml/components/logger.py b/esphomeyaml/components/logger.py index 6434237ce3..59a86807af 100644 --- a/esphomeyaml/components/logger.py +++ b/esphomeyaml/components/logger.py @@ -10,6 +10,8 @@ from esphomeyaml.core import EsphomeyamlError, Lambda, CORE from esphomeyaml.cpp_generator import Pvariable, RawExpression, add, process_lambda, statement from esphomeyaml.cpp_types import App, Component, esphomelib_ns, global_ns +from esphomeyaml.py_compat import text_type + LOG_LEVELS = { 'NONE': global_ns.ESPHOMELIB_LOG_LEVEL_NONE, 'ERROR': global_ns.ESPHOMELIB_LOG_LEVEL_ERROR, @@ -37,7 +39,7 @@ is_log_level = cv.one_of(*LOG_LEVELS, upper=True) def validate_local_no_higher_than_global(value): global_level = value.get(CONF_LEVEL, 'DEBUG') - for tag, level in value.get(CONF_LOGS, {}).iteritems(): + for tag, level in value.get(CONF_LOGS, {}).items(): if LOG_LEVEL_SEVERITY.index(level) > LOG_LEVEL_SEVERITY.index(global_level): raise EsphomeyamlError(u"The local log level {} for {} must be less severe than the " u"global log level {}.".format(level, tag, global_level)) @@ -64,7 +66,7 @@ def to_code(config): add(log.set_tx_buffer_size(config[CONF_TX_BUFFER_SIZE])) if CONF_LEVEL in config: add(log.set_global_log_level(LOG_LEVELS[config[CONF_LEVEL]])) - for tag, level in config.get(CONF_LOGS, {}).iteritems(): + for tag, level in config.get(CONF_LOGS, {}).items(): add(log.set_log_level(tag, LOG_LEVELS[level])) @@ -120,7 +122,7 @@ def validate_printf(value): [cCdiouxXeEfgGaAnpsSZ] # type ) | # OR %%) # literal "%%" - """ + """ # noqa matches = re.findall(cfmt, value[CONF_FORMAT], flags=re.X) if len(matches) != len(value[CONF_ARGS]): raise vol.Invalid(u"Found {} printf-patterns ({}), but {} args were given!" @@ -140,9 +142,9 @@ LOGGER_LOG_ACTION_SCHEMA = vol.All(maybe_simple_message({ @ACTION_REGISTRY.register(CONF_LOGGER_LOG, LOGGER_LOG_ACTION_SCHEMA) def logger_log_action_to_code(config, action_id, arg_type, template_arg): esp_log = LOG_LEVEL_TO_ESP_LOG[config[CONF_LEVEL]] - args = [RawExpression(unicode(x)) for x in config[CONF_ARGS]] + args = [RawExpression(text_type(x)) for x in config[CONF_ARGS]] - text = unicode(statement(esp_log(config[CONF_TAG], config[CONF_FORMAT], *args))) + text = text_type(statement(esp_log(config[CONF_TAG], config[CONF_FORMAT], *args))) for lambda_ in process_lambda(Lambda(text), [(arg_type, 'x')]): yield None diff --git a/esphomeyaml/components/ota.py b/esphomeyaml/components/ota.py index 65c314b56d..deac3d28d8 100644 --- a/esphomeyaml/components/ota.py +++ b/esphomeyaml/components/ota.py @@ -36,7 +36,7 @@ def get_port(config): return config[CONF_OTA][CONF_PORT] if CORE.is_esp32: return 3232 - elif CORE.is_esp8266: + if CORE.is_esp8266: return 8266 raise NotImplementedError @@ -52,6 +52,6 @@ REQUIRED_BUILD_FLAGS = '-DUSE_NEW_OTA' def lib_deps(config): if CORE.is_esp32: return ['Update', 'ESPmDNS'] - elif CORE.is_esp8266: + if CORE.is_esp8266: return ['Hash', 'ESP8266mDNS'] raise NotImplementedError diff --git a/esphomeyaml/components/output/custom.py b/esphomeyaml/components/output/custom.py index 4744ba16eb..6343a52853 100644 --- a/esphomeyaml/components/output/custom.py +++ b/esphomeyaml/components/output/custom.py @@ -37,7 +37,7 @@ def validate_custom_output(value): value[CONF_TYPE] = type if type == 'binary': return BINARY_SCHEMA(value) - elif type == 'float': + if type == 'float': return FLOAT_SCHEMA(value) raise vol.Invalid("type must either be binary or float, not {}!".format(type)) diff --git a/esphomeyaml/components/remote_receiver.py b/esphomeyaml/components/remote_receiver.py index b77ec45bb3..d1d6147379 100644 --- a/esphomeyaml/components/remote_receiver.py +++ b/esphomeyaml/components/remote_receiver.py @@ -7,6 +7,7 @@ from esphomeyaml.const import CONF_BUFFER_SIZE, CONF_DUMP, CONF_FILTER, CONF_ID, from esphomeyaml.cpp_generator import Pvariable, add from esphomeyaml.cpp_helpers import gpio_input_pin_expression, setup_component from esphomeyaml.cpp_types import App, Component, esphomelib_ns +from esphomeyaml.py_compat import string_types remote_ns = esphomelib_ns.namespace('remote') MULTI_CONF = True @@ -30,7 +31,7 @@ DUMPERS = { def validate_dumpers_all(value): - if not isinstance(value, (str, unicode)): + if not isinstance(value, string_types): raise vol.Invalid("Not valid dumpers") if value.upper() == "ALL": return list(sorted(list(DUMPERS))) diff --git a/esphomeyaml/components/remote_transmitter.py b/esphomeyaml/components/remote_transmitter.py index 5502a900e6..f0ae3636fa 100644 --- a/esphomeyaml/components/remote_transmitter.py +++ b/esphomeyaml/components/remote_transmitter.py @@ -10,6 +10,7 @@ from esphomeyaml.core import HexInt from esphomeyaml.cpp_generator import Pvariable, add from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component from esphomeyaml.cpp_types import App, Component +from esphomeyaml.py_compat import text_type RemoteTransmitterComponent = remote_ns.class_('RemoteTransmitterComponent', RemoteControlComponentBase, Component) @@ -20,7 +21,7 @@ MULTI_CONF = True def validate_rc_switch_code(value): - if not isinstance(value, (str, unicode)): + if not isinstance(value, (str, text_type)): raise vol.Invalid("All RCSwitch codes must be in quotes ('')") for c in value: if c not in ('0', '1'): diff --git a/esphomeyaml/components/sensor/ads1115.py b/esphomeyaml/components/sensor/ads1115.py index a16f77d079..a5ae2eadbe 100644 --- a/esphomeyaml/components/sensor/ads1115.py +++ b/esphomeyaml/components/sensor/ads1115.py @@ -6,6 +6,7 @@ import esphomeyaml.config_validation as cv from esphomeyaml.const import CONF_ADS1115_ID, CONF_GAIN, CONF_MULTIPLEXER, CONF_NAME, \ CONF_UPDATE_INTERVAL from esphomeyaml.cpp_generator import get_variable +from esphomeyaml.py_compat import string_types DEPENDENCIES = ['ads1115'] @@ -35,7 +36,7 @@ GAIN = { def validate_gain(value): if isinstance(value, float): value = u'{:0.03f}'.format(value) - elif not isinstance(value, (str, unicode)): + elif not isinstance(value, string_types): raise vol.Invalid('invalid gain "{}"'.format(value)) return cv.one_of(*GAIN)(value) diff --git a/esphomeyaml/components/sensor/pmsx003.py b/esphomeyaml/components/sensor/pmsx003.py index 568abea685..1d05c34da5 100644 --- a/esphomeyaml/components/sensor/pmsx003.py +++ b/esphomeyaml/components/sensor/pmsx003.py @@ -36,7 +36,7 @@ SENSORS_TO_TYPE = { def validate_pmsx003_sensors(value): - for key, types in SENSORS_TO_TYPE.iteritems(): + for key, types in SENSORS_TO_TYPE.items(): if key in value and value[CONF_TYPE] not in types: raise vol.Invalid(u"{} does not have {} sensor!".format(value[CONF_TYPE], key)) return value diff --git a/esphomeyaml/components/sensor/pulse_counter.py b/esphomeyaml/components/sensor/pulse_counter.py index ea27881ce1..1069575a30 100644 --- a/esphomeyaml/components/sensor/pulse_counter.py +++ b/esphomeyaml/components/sensor/pulse_counter.py @@ -35,8 +35,8 @@ def validate_internal_filter(value): if value.total_microseconds > 13: raise vol.Invalid("Maximum internal filter value for ESP32 is 13us") return value - else: - return cv.positive_time_period_microseconds(value) + + return cv.positive_time_period_microseconds(value) PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ diff --git a/esphomeyaml/components/substitutions.py b/esphomeyaml/components/substitutions.py index a5cf9010e7..65b3e08402 100644 --- a/esphomeyaml/components/substitutions.py +++ b/esphomeyaml/components/substitutions.py @@ -61,8 +61,8 @@ def _expand_substitutions(substitutions, value, path): if name.startswith(u'{') and name.endswith(u'}'): name = name[1:-1] if name not in substitutions: - _LOGGER.warn(u"Found '%s' (see %s) which looks like a substitution, but '%s' was not " - u"declared", orig_value, u'->'.join(str(x) for x in path), name) + _LOGGER.warning(u"Found '%s' (see %s) which looks like a substitution, but '%s' was " + u"not declared", orig_value, u'->'.join(str(x) for x in path), name) i = j continue @@ -82,7 +82,7 @@ def _substitute_item(substitutions, item, path): item[i] = sub elif isinstance(item, dict): replace_keys = [] - for k, v in item.iteritems(): + for k, v in item.items(): if path or k != CONF_SUBSTITUTIONS: sub = _substitute_item(substitutions, k, path + [k]) if sub is not None: @@ -116,7 +116,7 @@ def do_substitution_pass(config): key = '' try: replace_keys = [] - for key, value in substitutions.iteritems(): + for key, value in substitutions.items(): sub = validate_substitution_key(key) if sub != key: replace_keys.append((key, sub)) diff --git a/esphomeyaml/components/switch/remote_transmitter.py b/esphomeyaml/components/switch/remote_transmitter.py index d396945ec0..5afc018bda 100644 --- a/esphomeyaml/components/switch/remote_transmitter.py +++ b/esphomeyaml/components/switch/remote_transmitter.py @@ -85,15 +85,15 @@ def transmitter_base(full_config): if key == CONF_LG: return LGTransmitter.new(name, config[CONF_DATA], config[CONF_NBITS]) - elif key == CONF_NEC: + if key == CONF_NEC: return NECTransmitter.new(name, config[CONF_ADDRESS], config[CONF_COMMAND]) - elif key == CONF_PANASONIC: + if key == CONF_PANASONIC: return PanasonicTransmitter.new(name, config[CONF_ADDRESS], config[CONF_COMMAND]) - elif key == CONF_SAMSUNG: + if key == CONF_SAMSUNG: return SamsungTransmitter.new(name, config[CONF_DATA]) - elif key == CONF_SONY: + if key == CONF_SONY: return SonyTransmitter.new(name, config[CONF_DATA], config[CONF_NBITS]) - elif key == CONF_RAW: + if key == CONF_RAW: if isinstance(config, dict): data = config[CONF_DATA] carrier_frequency = config.get(CONF_CARRIER_FREQUENCY) @@ -102,29 +102,29 @@ def transmitter_base(full_config): carrier_frequency = None return RawTransmitter.new(name, ArrayInitializer(*data, multiline=False), carrier_frequency) - elif key == CONF_RC_SWITCH_RAW: + if key == CONF_RC_SWITCH_RAW: return RCSwitchRawTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), binary_code(config[CONF_CODE]), len(config[CONF_CODE])) - elif key == CONF_RC_SWITCH_TYPE_A: + if key == CONF_RC_SWITCH_TYPE_A: return RCSwitchTypeATransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), binary_code(config[CONF_GROUP]), binary_code(config[CONF_DEVICE]), config[CONF_STATE]) - elif key == CONF_RC_SWITCH_TYPE_B: + if key == CONF_RC_SWITCH_TYPE_B: return RCSwitchTypeBTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), config[CONF_ADDRESS], config[CONF_CHANNEL], config[CONF_STATE]) - elif key == CONF_RC_SWITCH_TYPE_C: + if key == CONF_RC_SWITCH_TYPE_C: return RCSwitchTypeCTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), ord(config[CONF_FAMILY][0]) - ord('a'), config[CONF_GROUP], config[CONF_DEVICE], config[CONF_STATE]) - elif key == CONF_RC_SWITCH_TYPE_D: + if key == CONF_RC_SWITCH_TYPE_D: return RCSwitchTypeDTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), ord(config[CONF_GROUP][0]) - ord('a'), config[CONF_DEVICE], config[CONF_STATE]) - else: - raise NotImplementedError("Unknown transmitter type {}".format(config)) + + raise NotImplementedError("Unknown transmitter type {}".format(config)) def to_code(config): diff --git a/esphomeyaml/components/switch/uart.py b/esphomeyaml/components/switch/uart.py index c0747a0c9b..6a5dfdeba4 100644 --- a/esphomeyaml/components/switch/uart.py +++ b/esphomeyaml/components/switch/uart.py @@ -7,6 +7,7 @@ from esphomeyaml.const import CONF_DATA, CONF_INVERTED, CONF_MAKE_ID, CONF_NAME, from esphomeyaml.core import HexInt from esphomeyaml.cpp_generator import ArrayInitializer, get_variable, variable from esphomeyaml.cpp_types import App, Application +from esphomeyaml.py_compat import text_type DEPENDENCIES = ['uart'] @@ -15,11 +16,11 @@ UARTSwitch = switch.switch_ns.class_('UARTSwitch', switch.Switch, uart.UARTDevic def validate_data(value): - if isinstance(value, unicode): + if isinstance(value, text_type): return value.encode('utf-8') - elif isinstance(value, str): + if isinstance(value, str): return value - elif isinstance(value, list): + if isinstance(value, list): return vol.Schema([cv.hex_uint8_t])(value) raise vol.Invalid("data must either be a string wrapped in quotes or a list of bytes") diff --git a/esphomeyaml/components/time/__init__.py b/esphomeyaml/components/time/__init__.py index 9e27c15b11..8b78b0dfaa 100644 --- a/esphomeyaml/components/time/__init__.py +++ b/esphomeyaml/components/time/__init__.py @@ -11,6 +11,7 @@ from esphomeyaml.const import CONF_CRON, CONF_DAYS_OF_MONTH, CONF_DAYS_OF_WEEK, from esphomeyaml.core import CORE from esphomeyaml.cpp_generator import add, Pvariable, ArrayInitializer from esphomeyaml.cpp_types import esphomelib_ns, Component, NoArg, Trigger, App +from esphomeyaml.py_compat import string_types _LOGGER = logging.getLogger(__name__) @@ -30,9 +31,9 @@ def _tz_timedelta(td): offset_second = int(abs(td.total_seconds())) % 60 if offset_hour == 0 and offset_minute == 0 and offset_second == 0: return '0' - elif offset_minute == 0 and offset_second == 0: + if offset_minute == 0 and offset_second == 0: return '{}'.format(offset_hour) - elif offset_second == 0: + if offset_second == 0: return '{}:{}'.format(offset_hour, offset_minute) return '{}:{}:{}'.format(offset_hour, offset_minute, offset_second) @@ -119,7 +120,7 @@ def detect_tz(): import pytz except ImportError: raise vol.Invalid("No timezone specified and 'tzlocal' not installed. To automatically " - "detect the timezone please install tzlocal (pip2 install tzlocal)") + "detect the timezone please install tzlocal (pip install tzlocal)") try: tz = tzlocal.get_localzone() except pytz.exceptions.UnknownTimeZoneError: @@ -131,7 +132,7 @@ def detect_tz(): def _parse_cron_int(value, special_mapping, message): special_mapping = special_mapping or {} - if isinstance(value, (str, unicode)) and value in special_mapping: + if isinstance(value, string_types) and value in special_mapping: return special_mapping[value] try: return int(value) @@ -140,7 +141,7 @@ def _parse_cron_int(value, special_mapping, message): def _parse_cron_part(part, min_value, max_value, special_mapping): - if part == '*' or part == '?': + if part in ('*', '?'): return set(x for x in range(min_value, max_value + 1)) if '/' in part: data = part.split('/') diff --git a/esphomeyaml/components/wifi.py b/esphomeyaml/components/wifi.py index f67565059f..eaf5f8a849 100644 --- a/esphomeyaml/components/wifi.py +++ b/esphomeyaml/components/wifi.py @@ -171,6 +171,6 @@ def to_code(config): def lib_deps(config): if CORE.is_esp8266: return 'ESP8266WiFi' - elif CORE.is_esp32: + if CORE.is_esp32: return None raise NotImplementedError diff --git a/esphomeyaml/config.py b/esphomeyaml/config.py index 29653efa96..7be8d19908 100644 --- a/esphomeyaml/config.py +++ b/esphomeyaml/config.py @@ -13,6 +13,7 @@ from esphomeyaml.components import substitutions from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_PLATFORM, ESP_PLATFORMS from esphomeyaml.core import CORE, EsphomeyamlError from esphomeyaml.helpers import color, indent +from esphomeyaml.py_compat import text_type from esphomeyaml.util import safe_print # pylint: disable=unused-import, wrong-import-order @@ -50,7 +51,7 @@ def is_platform_component(component): def iter_components(config): - for domain, conf in config.iteritems(): + for domain, conf in config.items(): if domain == CONF_ESPHOMEYAML: yield CONF_ESPHOMEYAML, core_config, conf continue @@ -67,7 +68,7 @@ def iter_components(config): yield p_name, platform, p_config -ConfigPath = List[Union[basestring, int]] +ConfigPath = List[Union[str, int]] def _path_begins_with_(path, other): # type: (ConfigPath, ConfigPath) -> bool @@ -90,8 +91,8 @@ class Config(OrderedDict): def add_error(self, message, path): # type: (basestring, ConfigPath) -> None - if not isinstance(message, unicode): - message = unicode(message) + if not isinstance(message, text_type): + message = text_type(message) self.errors.append((message, path)) def add_domain(self, path, name): @@ -158,7 +159,7 @@ def iter_ids(config, path=None): for result in iter_ids(item, path + [i]): yield result elif isinstance(config, dict): - for key, value in config.iteritems(): + for key, value in config.items(): for result in iter_ids(value, path + [key]): yield result @@ -229,7 +230,7 @@ def validate_config(config): result.add_domain([CONF_ESPHOMEYAML], CONF_ESPHOMEYAML) result[CONF_ESPHOMEYAML] = config[CONF_ESPHOMEYAML] - for domain, conf in config.iteritems(): + for domain, conf in config.items(): domain = str(domain) if domain == CONF_ESPHOMEYAML or domain.startswith(u'.'): skip_paths.append([domain]) @@ -337,7 +338,7 @@ def validate_config(config): except vol.Invalid as ex: _comp_error(ex, [CONF_ESPHOMEYAML]) - for domain, conf in result.iteritems(): + for domain, conf in result.items(): domain = str(domain) if [domain] in skip_paths: continue @@ -401,7 +402,7 @@ def humanize_error(config, validation_error): offending_item_summary = json.dumps(offending_item_summary) except (TypeError, ValueError): pass - validation_error = unicode(validation_error) + validation_error = text_type(validation_error) m = re.match(r'^(.*?)\s*(?:for dictionary value )?@ data\[.*$', validation_error) if m is not None: validation_error = m.group(1) @@ -514,7 +515,7 @@ def dump_dict(config, path, at_root=True): ret += u'{}' multiline = False - for k in conf.iterkeys(): + for k in conf.keys(): path_ = path + [k] error = config.get_error_for_path(path_) if error is not None: @@ -543,9 +544,9 @@ def dump_dict(config, path, at_root=True): conf = u'|-\n' + indent(conf) error = config.get_error_for_path(path) col = 'bold_red' if error else 'white' - ret += color(col, unicode(conf)) + ret += color(col, text_type(conf)) elif isinstance(conf, core.Lambda): - conf = u'!lambda |-\n' + indent(unicode(conf.value)) + conf = u'!lambda |-\n' + indent(text_type(conf.value)) error = config.get_error_for_path(path) col = 'bold_red' if error else 'white' ret += color(col, conf) @@ -554,7 +555,7 @@ def dump_dict(config, path, at_root=True): else: error = config.get_error_for_path(path) col = 'bold_red' if error else 'white' - ret += color(col, unicode(conf)) + ret += color(col, text_type(conf)) multiline = u'\n' in ret return ret, multiline @@ -571,7 +572,7 @@ def strip_default_ids(config): config.remove(x) elif isinstance(config, dict): to_remove = [] - for k, v in config.iteritems(): + for k, v in config.items(): v = config[k] = strip_default_ids(v) if isinstance(v, core.ID) and not v.is_manual: to_remove.append(k) diff --git a/esphomeyaml/config_validation.py b/esphomeyaml/config_validation.py index 0092296d27..8fcd382437 100644 --- a/esphomeyaml/config_validation.py +++ b/esphomeyaml/config_validation.py @@ -16,6 +16,7 @@ from esphomeyaml.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOV ESP_PLATFORM_ESP8266 from esphomeyaml.core import CORE, HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \ TimePeriodMilliseconds, TimePeriodSeconds +from esphomeyaml.py_compat import text_type, string_types, integer_types _LOGGER = logging.getLogger(__name__) @@ -51,7 +52,7 @@ RESERVED_IDS = [ def alphanumeric(value): if value is None: raise vol.Invalid("string value is None") - value = unicode(value) + value = text_type(value) if not value.isalnum(): raise vol.Invalid("string value is not alphanumeric") return value @@ -70,13 +71,13 @@ def string(value): if isinstance(value, (dict, list)): raise vol.Invalid("string value cannot be dictionary or list.") if value is not None: - return unicode(value) + return text_type(value) raise vol.Invalid("string value is None") def string_strict(value): """Strictly only allow strings.""" - if isinstance(value, (str, unicode)): + if isinstance(value, string_types): return value raise vol.Invalid("Must be string, got {}. did you forget putting quotes " "around the value?".format(type(value))) @@ -138,7 +139,7 @@ def ensure_dict(value): def hex_int_(value): - if isinstance(value, (int, long)): + if isinstance(value, integer_types): return HexInt(value) value = string_strict(value).lower() if value.startswith('0x'): @@ -147,7 +148,7 @@ def hex_int_(value): def int_(value): - if isinstance(value, (int, long)): + if isinstance(value, integer_types): return value value = string_strict(value).lower() if value.startswith('0x'): @@ -310,9 +311,9 @@ def time_period_str_colon(value): def time_period_str_unit(value): """Validate and transform time period with time unit and integer value.""" if isinstance(value, int): - raise vol.Invalid("Don't know what '{}' means as it has no time *unit*! Did you mean " - "'{}s'?".format(value, value)) - elif not isinstance(value, (str, unicode)): + raise vol.Invalid("Don't know what '{0}' means as it has no time *unit*! Did you mean " + "'{0}s'?".format(value)) + elif not isinstance(value, string_types): raise vol.Invalid("Expected string for time period with unit.") unit_to_kwarg = { @@ -489,7 +490,7 @@ def ssid(value): def ipv4(value): if isinstance(value, list): parts = value - elif isinstance(value, basestring): + elif isinstance(value, string_types): parts = value.split('.') elif isinstance(value, IPAddress): return value @@ -579,7 +580,7 @@ i2c_address = hex_uint8_t def percentage(value): - has_percent_sign = isinstance(value, (str, unicode)) and value.endswith('%') + has_percent_sign = isinstance(value, string_types) and value.endswith('%') if has_percent_sign: value = float(value[:-1].rstrip()) / 100.0 if value > 1: @@ -591,7 +592,7 @@ def percentage(value): def percentage_int(value): - if isinstance(value, (str, unicode)) and value.endswith('%'): + if isinstance(value, string_types) and value.endswith('%'): value = int(value[:-1].rstrip()) return value diff --git a/esphomeyaml/core.py b/esphomeyaml/core.py index afa7a52035..7518f62802 100644 --- a/esphomeyaml/core.py +++ b/esphomeyaml/core.py @@ -13,15 +13,22 @@ from esphomeyaml.helpers import ensure_unique_string # pylint: disable=unused-import, wrong-import-order from typing import Any, Dict, List # noqa +from esphomeyaml.py_compat import integer_types, IS_PY2 + _LOGGER = logging.getLogger(__name__) class EsphomeyamlError(Exception): """General esphomeyaml exception occurred.""" - pass -class HexInt(long): +if IS_PY2: + base_int = long +else: + base_int = int + + +class HexInt(base_int): def __str__(self): if 0 <= self <= 255: return "0x{:02X}".format(self) @@ -55,7 +62,7 @@ class MACAddress(object): def is_approximately_integer(value): - if isinstance(value, (int, long)): + if isinstance(value, integer_types): return True return abs(value - round(value)) < 0.001 @@ -379,7 +386,7 @@ class EsphomeyamlCore(object): task, domain = self.pending_tasks.popleft() _LOGGER.debug("Executing task for domain=%s", domain) try: - task.next() + next(task) self.pending_tasks.append((task, domain)) except StopIteration: _LOGGER.debug(" -> %s finished", domain) @@ -404,7 +411,7 @@ class EsphomeyamlCore(object): def get_variable_with_full_id(self, id): while True: if id in self.variables: - for k, v in self.variables.iteritems(): + for k, v in self.variables.items(): if k == id: yield (k, v) return diff --git a/esphomeyaml/core_config.py b/esphomeyaml/core_config.py index b3ba6f384f..6c9bcbf05f 100644 --- a/esphomeyaml/core_config.py +++ b/esphomeyaml/core_config.py @@ -15,6 +15,7 @@ from esphomeyaml.const import ARDUINO_VERSION_ESP32_DEV, ARDUINO_VERSION_ESP8266 from esphomeyaml.core import CORE, EsphomeyamlError from esphomeyaml.cpp_generator import Pvariable, RawExpression, add from esphomeyaml.cpp_types import App, NoArg, const_char_ptr, esphomelib_ns +from esphomeyaml.py_compat import text_type _LOGGER = logging.getLogger(__name__) @@ -52,12 +53,12 @@ def validate_simple_esphomelib_version(value): CONF_REPOSITORY: LIBRARY_URI_REPO, CONF_TAG: 'v' + ESPHOMELIB_VERSION, } - elif value.upper() == 'DEV': + if value.upper() == 'DEV': return { CONF_REPOSITORY: LIBRARY_URI_REPO, CONF_BRANCH: 'dev' } - elif VERSION_REGEX.match(value) is not None: + if VERSION_REGEX.match(value) is not None: return { CONF_REPOSITORY: LIBRARY_URI_REPO, CONF_TAG: 'v' + value, @@ -139,7 +140,7 @@ def validate_arduino_version(value): if value_ in PLATFORMIO_ESP8266_LUT: return PLATFORMIO_ESP8266_LUT[value_] return value - elif CORE.is_esp32: + if CORE.is_esp32: if VERSION_REGEX.match(value) is not None and value_ not in PLATFORMIO_ESP32_LUT: raise vol.Invalid("Unfortunately the arduino framework version '{}' is unsupported " "at this time. You can override this by manually using " @@ -206,7 +207,7 @@ def preload_core_config(config): CORE.build_path = CORE.relative_path( cv.string(core_conf.get(CONF_BUILD_PATH, default_build_path()))) except vol.Invalid as e: - raise EsphomeyamlError(unicode(e)) + raise EsphomeyamlError(text_type(e)) def to_code(config): diff --git a/esphomeyaml/cpp_generator.py b/esphomeyaml/cpp_generator.py index 16400224c0..12844a9153 100644 --- a/esphomeyaml/cpp_generator.py +++ b/esphomeyaml/cpp_generator.py @@ -7,6 +7,7 @@ from esphomeyaml.helpers import cpp_string_escape, indent_all_but_first_and_last # pylint: disable=unused-import, wrong-import-order from typing import Any, Generator, List, Optional, Tuple, Union # noqa from esphomeyaml.core import ID # noqa +from esphomeyaml.py_compat import text_type, string_types, integer_types class Expression(object): @@ -28,7 +29,7 @@ class Expression(object): return self.required -SafeExpType = Union[Expression, bool, str, unicode, int, long, float, TimePeriod] +SafeExpType = Union[Expression, bool, str, text_type, int, float, TimePeriod] class RawExpression(Expression): @@ -73,7 +74,7 @@ class ExpressionList(Expression): self.args.append(exp) def __str__(self): - text = u", ".join(unicode(x) for x in self.args) + text = u", ".join(text_type(x) for x in self.args) return indent_all_but_first_and_last(text) @@ -115,7 +116,7 @@ class StructInitializer(Expression): if not isinstance(args, OrderedDict): args = OrderedDict(args) self.args = OrderedDict() - for key, value in args.iteritems(): + for key, value in args.items(): if value is None: continue exp = safe_exp(value) @@ -124,7 +125,7 @@ class StructInitializer(Expression): def __str__(self): cpp = u'{}{{\n'.format(self.base) - for key, value in self.args.iteritems(): + for key, value in self.args.items(): cpp += u' .{} = {},\n'.format(key, value) cpp += u'}' return cpp @@ -176,7 +177,7 @@ class ParameterListExpression(Expression): self.requires.append(parameter) def __str__(self): - return u", ".join(unicode(x) for x in self.parameters) + return u", ".join(text_type(x) for x in self.parameters) class LambdaExpression(Expression): @@ -203,7 +204,7 @@ class LambdaExpression(Expression): @property def content(self): - return u''.join(unicode(part) for part in self.parts) + return u''.join(text_type(part) for part in self.parts) class Literal(Expression): @@ -232,7 +233,7 @@ class IntLiteral(Literal): return u'{}UL'.format(self.i) if self.i < -2147483648: return u'{}LL'.format(self.i) - return unicode(self.i) + return text_type(self.i) class BoolLiteral(Literal): @@ -268,21 +269,21 @@ def safe_exp(obj # type: Union[Expression, bool, str, unicode, int, long, float # type: (...) -> Expression if isinstance(obj, Expression): return obj - elif isinstance(obj, bool): + if isinstance(obj, bool): return BoolLiteral(obj) - elif isinstance(obj, (str, unicode)): + if isinstance(obj, string_types): return StringLiteral(obj) - elif isinstance(obj, HexInt): + if isinstance(obj, HexInt): return HexIntLiteral(obj) - elif isinstance(obj, (int, long)): + if isinstance(obj, integer_types): return IntLiteral(obj) - elif isinstance(obj, float): + if isinstance(obj, float): return FloatLiteral(obj) - elif isinstance(obj, TimePeriodMicroseconds): + if isinstance(obj, TimePeriodMicroseconds): return IntLiteral(int(obj.total_microseconds)) - elif isinstance(obj, TimePeriodMilliseconds): + if isinstance(obj, TimePeriodMilliseconds): return IntLiteral(int(obj.total_milliseconds)) - elif isinstance(obj, TimePeriodSeconds): + if isinstance(obj, TimePeriodSeconds): return IntLiteral(int(obj.total_seconds)) raise ValueError(u"Object is not an expression", obj) @@ -441,7 +442,7 @@ class MockObj(Expression): return obj def __str__(self): # type: () -> unicode - return unicode(self.base) + return text_type(self.base) def require(self): # type: () -> None self.required = True diff --git a/esphomeyaml/cpp_helpers.py b/esphomeyaml/cpp_helpers.py index 030e4b95e2..c96dce6977 100644 --- a/esphomeyaml/cpp_helpers.py +++ b/esphomeyaml/cpp_helpers.py @@ -20,11 +20,11 @@ def generic_gpio_pin_expression_(conf, mock_obj, default_mode): mode = pcf8574.PCF8675_GPIO_MODES[conf.get(CONF_MODE, u'INPUT')] yield hub.make_input_pin(number, mode, inverted) return - elif default_mode == u'OUTPUT': + if default_mode == u'OUTPUT': yield hub.make_output_pin(number, inverted) return - else: - raise EsphomeyamlError(u"Unknown default mode {}".format(default_mode)) + + raise EsphomeyamlError(u"Unknown default mode {}".format(default_mode)) if len(conf) == 1: yield IntLiteral(number) return diff --git a/esphomeyaml/dashboard/dashboard.py b/esphomeyaml/dashboard/dashboard.py index ae9b1518ae..d21a7e4664 100644 --- a/esphomeyaml/dashboard/dashboard.py +++ b/esphomeyaml/dashboard/dashboard.py @@ -176,7 +176,7 @@ class WizardRequestHandler(BaseHandler): if not self.is_authenticated(): self.redirect('/login') return - kwargs = {k: ''.join(v) for k, v in self.request.arguments.iteritems()} + kwargs = {k: ''.join(v) for k, v in self.request.arguments.items()} destination = os.path.join(CONFIG_DIR, kwargs['name'] + '.yaml') wizard.wizard_write(path=destination, **kwargs) self.redirect('/?begin=True') @@ -436,7 +436,7 @@ class LoginHandler(BaseHandler): self.redirect('/') return except Exception as err: # pylint: disable=broad-except - _LOGGER.warn("Error during Hass.io auth request: %s", err) + _LOGGER.warning("Error during Hass.io auth request: %s", err) self.set_status(500) self.render_hassio_login(error="Internal server error") return diff --git a/esphomeyaml/espota.py b/esphomeyaml/espota.py index e6babdbf3d..9892c74ebc 100755 --- a/esphomeyaml/espota.py +++ b/esphomeyaml/espota.py @@ -37,6 +37,7 @@ import hashlib import logging +# pylint: disable=deprecated-module import optparse import os import random diff --git a/esphomeyaml/helpers.py b/esphomeyaml/helpers.py index b3cb948245..b7afe6cfca 100644 --- a/esphomeyaml/helpers.py +++ b/esphomeyaml/helpers.py @@ -6,6 +6,8 @@ import os import socket import subprocess +from esphomeyaml.py_compat import text_type, IS_PY2 + _LOGGER = logging.getLogger(__name__) @@ -39,14 +41,16 @@ def indent(text, padding=u' '): # From https://stackoverflow.com/a/14945195/8924614 def cpp_string_escape(string, encoding='utf-8'): - if isinstance(string, unicode): + if isinstance(string, text_type): string = string.encode(encoding) result = '' for character in string: - if not (32 <= ord(character) < 127) or character in ('\\', '"'): - result += '\\%03o' % ord(character) + if IS_PY2: + character = ord(character) + if not (32 <= character < 127) or character in ('\\', '"'): + result += '\\%03o' % character else: - result += character + result += chr(character) return '"' + result + '"' diff --git a/esphomeyaml/pins.py b/esphomeyaml/pins.py index 0cc47714c3..e718bb03c9 100644 --- a/esphomeyaml/pins.py +++ b/esphomeyaml/pins.py @@ -246,7 +246,7 @@ def validate_gpio_pin(value): _LOGGER.warning(u"ESP32: Pin %s (20, 24, 28-31) can usually not be used. " u"Be warned.", value) return value - elif CORE.is_esp8266: + if CORE.is_esp8266: if 6 <= value <= 11: _LOGGER.warning(u"ESP8266: Pin %s (6-11) might already be used by the " u"flash interface. Be warned.", value) @@ -264,7 +264,7 @@ def input_pullup_pin(value): value = input_pin(value) if CORE.is_esp32: return output_pin(value) - elif CORE.is_esp8266: + if CORE.is_esp8266: if value == 0: raise vol.Invalid("GPIO Pin 0 does not support pullup pin mode. " "Please choose another pin.") @@ -279,7 +279,7 @@ def output_pin(value): raise vol.Invalid(u"ESP32: GPIO{} (34-39) can only be used as an " u"input pin.".format(value)) return value - elif CORE.is_esp8266: + if CORE.is_esp8266: return value raise NotImplementedError @@ -316,7 +316,7 @@ PIN_MODES_ESP32 = [ def pin_mode(value): if CORE.is_esp32: return cv.one_of(*PIN_MODES_ESP32, upper=True)(value) - elif CORE.is_esp8266: + if CORE.is_esp8266: return cv.one_of(*PIN_MODES_ESP8266, upper=True)(value) raise NotImplementedError diff --git a/esphomeyaml/py_compat.py b/esphomeyaml/py_compat.py new file mode 100644 index 0000000000..c3051fc19f --- /dev/null +++ b/esphomeyaml/py_compat.py @@ -0,0 +1,25 @@ +import sys + +PYTHON_MAJOR = sys.version_info[0] +IS_PY2 = PYTHON_MAJOR == 2 +IS_PY3 = PYTHON_MAJOR == 3 + + +# pylint: disable=no-else-return +def safe_input(line): + if IS_PY2: + return raw_input(line) + else: + return input(line) + + +if IS_PY2: + text_type = unicode + string_types = (str, unicode) + integer_types = (int, long) + binary_type = str +else: + text_type = str + string_types = (str,) + integer_types = (int,) + binary_type = bytes diff --git a/esphomeyaml/storage_json.py b/esphomeyaml/storage_json.py index 9bc095908e..82caafc490 100644 --- a/esphomeyaml/storage_json.py +++ b/esphomeyaml/storage_json.py @@ -272,13 +272,13 @@ class CheckForUpdateThread(threading.Thread): remote_version = StrictVersion(storage.remote_version) self_version = StrictVersion(const.__version__) if remote_version > self_version: - _LOGGER.warn("*" * 80) - _LOGGER.warn("A new version of esphomeyaml is available: %s (this is %s)", - self.format_version(remote_version), self.format_version(self_version)) - _LOGGER.warn("Changelog: %s/esphomeyaml/changelog/index.html", self.docs_base) - _LOGGER.warn("Update Instructions: %s/esphomeyaml/guides/faq.html" - "#how-do-i-update-to-the-latest-version", self.docs_base) - _LOGGER.warn("*" * 80) + _LOGGER.warning("*" * 80) + _LOGGER.warning("A new version of esphomeyaml is available: %s (this is %s)", + self.format_version(remote_version), self.format_version(self_version)) + _LOGGER.warning("Changelog: %s/esphomeyaml/changelog/index.html", self.docs_base) + _LOGGER.warning("Update Instructions: %s/esphomeyaml/guides/faq.html" + "#how-do-i-update-to-the-latest-version", self.docs_base) + _LOGGER.warning("*" * 80) def run(self): try: diff --git a/esphomeyaml/wizard.py b/esphomeyaml/wizard.py index 7cab252ce1..8c1dd4fc81 100644 --- a/esphomeyaml/wizard.py +++ b/esphomeyaml/wizard.py @@ -11,31 +11,32 @@ from esphomeyaml.const import ESP_PLATFORMS, ESP_PLATFORM_ESP32, ESP_PLATFORM_ES from esphomeyaml.helpers import color # pylint: disable=anomalous-backslash-in-string from esphomeyaml.pins import ESP32_BOARD_PINS, ESP8266_BOARD_PINS +from esphomeyaml.py_compat import safe_input, text_type from esphomeyaml.storage_json import StorageJSON, ext_storage_path from esphomeyaml.util import safe_print -CORE_BIG = """ _____ ____ _____ ______ +CORE_BIG = r""" _____ ____ _____ ______ / ____/ __ \| __ \| ____| | | | | | | |__) | |__ | | | | | | _ /| __| | |___| |__| | | \ \| |____ \_____\____/|_| \_\______| """ -ESP_BIG = """ ______ _____ _____ +ESP_BIG = r""" ______ _____ _____ | ____|/ ____| __ \\ | |__ | (___ | |__) | | __| \___ \| ___/ | |____ ____) | | |______|_____/|_| """ -WIFI_BIG = """ __ ___ ______ _ +WIFI_BIG = r""" __ ___ ______ _ \ \ / (_) ____(_) \ \ /\ / / _| |__ _ \ \/ \/ / | | __| | | \ /\ / | | | | | \/ \/ |_|_| |_| """ -OTA_BIG = """ ____ _______ +OTA_BIG = r""" ____ _______ / __ \__ __|/\\ | | | | | | / \\ | | | | | | / /\ \\ @@ -85,7 +86,7 @@ def wizard_write(path, **kwargs): storage.save(storage_path) -if os.getenv('ESPHOMEYAML_QUICKWIZARD', False): +if os.getenv('ESPHOMEYAML_QUICKWIZARD', ''): def sleep(time): pass else: @@ -104,12 +105,12 @@ def safe_print_step(step, big): def default_input(text, default): safe_print() safe_print(u"Press ENTER for default ({})".format(default)) - return raw_input(text.format(default)) or default + return safe_input(text.format(default)) or default # From https://stackoverflow.com/a/518232/8924614 def strip_accents(string): - return u''.join(c for c in unicodedata.normalize('NFD', unicode(string)) + return u''.join(c for c in unicodedata.normalize('NFD', text_type(string)) if unicodedata.category(c) != 'Mn') @@ -140,7 +141,7 @@ def wizard(path): color('bold_white', "livingroom"))) safe_print() sleep(1) - name = raw_input(color("bold_white", "(name): ")) + name = safe_input(color("bold_white", "(name): ")) while True: try: name = cv.valid_name(name) @@ -165,7 +166,7 @@ def wizard(path): sleep(0.5) safe_print() safe_print("Please enter either ESP32 or ESP8266.") - platform = raw_input(color("bold_white", "(ESP32/ESP8266): ")) + platform = safe_input(color("bold_white", "(ESP32/ESP8266): ")) try: platform = vol.All(vol.Upper, vol.Any(*ESP_PLATFORMS))(platform) break @@ -197,7 +198,7 @@ def wizard(path): safe_print("Options: {}".format(', '.join(boards))) while True: - board = raw_input(color("bold_white", "(board): ")) + board = safe_input(color("bold_white", "(board): ")) try: board = vol.All(vol.Lower, vol.Any(*boards))(board) break @@ -221,7 +222,7 @@ def wizard(path): sleep(1.5) safe_print("For example \"{}\".".format(color('bold_white', "Abraham Linksys"))) while True: - ssid = raw_input(color('bold_white', "(ssid): ")) + ssid = safe_input(color('bold_white', "(ssid): ")) try: ssid = cv.ssid(ssid) break @@ -241,7 +242,7 @@ def wizard(path): safe_print() safe_print("For example \"{}\"".format(color('bold_white', 'PASSWORD42'))) sleep(0.5) - psk = raw_input(color('bold_white', '(PSK): ')) + psk = safe_input(color('bold_white', '(PSK): ')) safe_print("Perfect! WiFi is now set up (you can create static IPs and so on later).") sleep(1.5) @@ -253,7 +254,7 @@ def wizard(path): safe_print() sleep(0.25) safe_print("Press ENTER for no password") - password = raw_input(color('bold_white', '(password): ')) + password = safe_input(color('bold_white', '(password): ')) wizard_write(path=path, name=name, platform=platform, board=board, ssid=ssid, psk=psk, password=password) diff --git a/esphomeyaml/writer.py b/esphomeyaml/writer.py index 27b3a0c157..25c8be609d 100644 --- a/esphomeyaml/writer.py +++ b/esphomeyaml/writer.py @@ -16,6 +16,7 @@ from esphomeyaml.core import CORE, EsphomeyamlError from esphomeyaml.core_config import VERSION_REGEX, LIBRARY_URI_REPO, GITHUB_ARCHIVE_ZIP from esphomeyaml.helpers import mkdir_p, run_system_command from esphomeyaml.pins import ESP8266_LD_SCRIPTS, ESP8266_FLASH_SIZES +from esphomeyaml.py_compat import IS_PY3, string_types from esphomeyaml.storage_json import StorageJSON, storage_path from esphomeyaml.util import safe_print @@ -71,7 +72,7 @@ def get_build_flags(key): flags = flags(conf) if flags is None: continue - if isinstance(flags, (str, unicode)): + if isinstance(flags, string_types): flags = [flags] build_flags |= set(flags) return build_flags @@ -114,14 +115,19 @@ def update_esphomelib_repo(): '--') if rc != 0: # local changes, cannot update - _LOGGER.warn("Local changes in esphomelib copy from git. Will not auto-update.") + _LOGGER.warning("Local changes in esphomelib copy from git. Will not auto-update.") return _LOGGER.info("Updating esphomelib copy from git (%s)", esphomelib_path) rc, stdout, _ = run_system_command('git', '-c', 'color.ui=always', '-C', esphomelib_path, 'pull', '--stat') if rc != 0: - _LOGGER.warn("Couldn't auto-update local git copy of esphomelib.") + _LOGGER.warning("Couldn't auto-update local git copy of esphomelib.") return + if IS_PY3: + try: + stdout = stdout.encode('utf-8') + except Exception: # pylint: disable=broad-except + pass safe_print(stdout.strip()) diff --git a/esphomeyaml/yaml_util.py b/esphomeyaml/yaml_util.py index be2c8cd558..071aec13d5 100644 --- a/esphomeyaml/yaml_util.py +++ b/esphomeyaml/yaml_util.py @@ -12,6 +12,7 @@ import yaml.constructor from esphomeyaml import core from esphomeyaml.core import EsphomeyamlError, HexInt, IPAddress, Lambda, MACAddress, TimePeriod +from esphomeyaml.py_compat import text_type, string_types _LOGGER = logging.getLogger(__name__) @@ -24,14 +25,10 @@ SECRET_YAML = u'secrets.yaml' class NodeListClass(list): """Wrapper class to be able to add attributes on a list.""" - pass - -class NodeStrClass(unicode): +class NodeStrClass(text_type): """Wrapper class to be able to add attributes on a string.""" - pass - class SafeLineLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors """Loader class that keeps track of line numbers.""" @@ -72,7 +69,7 @@ def custom_construct_pairs(loader, node): if not isinstance(obj, dict): raise EsphomeyamlError( "Expected mapping for anchored include tag, got {}".format(type(obj))) - for key, value in obj.iteritems(): + for key, value in obj.items(): pairs.append((key, value)) else: key_node, value_node = kv @@ -168,7 +165,7 @@ def _construct_seq(loader, node): def _add_reference(obj, loader, node): """Add file reference information to an object.""" - if isinstance(obj, (str, unicode)): + if isinstance(obj, string_types): obj = NodeStrClass(obj) if isinstance(obj, list): obj = NodeListClass(obj) @@ -184,7 +181,7 @@ def _env_var_yaml(_, node): # Check for a default value if len(args) > 1: return os.getenv(args[0], u' '.join(args[1:])) - elif args[0] in os.environ: + if args[0] in os.environ: return os.environ[args[0]] raise EsphomeyamlError(u"Environment variable {} not defined.".format(node.value)) @@ -268,7 +265,7 @@ def _secret_yaml(loader, node): def _lambda(loader, node): - return Lambda(unicode(node.value)) + return Lambda(text_type(node.value)) yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, _ordered_dict) @@ -295,7 +292,7 @@ def represent_odict(dump, tag, mapping, flow_style=None): dump.represented_objects[dump.alias_key] = node best_style = True if hasattr(mapping, 'items'): - mapping = mapping.items() + mapping = list(mapping.items()) for item_key, item_value in mapping: node_key = dump.represent_data(item_key) node_value = dump.represent_data(item_value) @@ -372,7 +369,7 @@ yaml.SafeDumper.add_representer( dumper.represent_sequence('tag:yaml.org,2002:seq', value) ) -yaml.SafeDumper.add_representer(unicode, unicode_representer) +yaml.SafeDumper.add_representer(text_type, unicode_representer) yaml.SafeDumper.add_representer(HexInt, hex_int_representer) yaml.SafeDumper.add_representer(IPAddress, stringify_representer) yaml.SafeDumper.add_representer(MACAddress, stringify_representer) diff --git a/pylintrc b/pylintrc index 19d7d0b6c8..6532088f18 100644 --- a/pylintrc +++ b/pylintrc @@ -19,6 +19,8 @@ disable= cyclic-import, redefined-builtin, undefined-loop-variable, + useless-object-inheritance, + stop-iteration-return, additional-builtins= diff --git a/setup.py b/setup.py index 9b664d78cd..3288c3f7d0 100755 --- a/setup.py +++ b/setup.py @@ -56,7 +56,7 @@ setup( zip_safe=False, platforms='any', test_suite='tests', - python_requires='>=2.7,<3', + python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,<4.0', install_requires=REQUIRES, keywords=['home', 'automation'], entry_points={