From 2ea826277fd0fdce0e28d2595431755c7122f457 Mon Sep 17 00:00:00 2001 From: Cherie Williams Date: Wed, 7 Sep 2016 15:31:56 -0700 Subject: [PATCH 1/4] Send a more accurate error message --- pebble_tool/commands/sdk/project/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pebble_tool/commands/sdk/project/__init__.py b/pebble_tool/commands/sdk/project/__init__.py index 6eaac89..71d313d 100644 --- a/pebble_tool/commands/sdk/project/__init__.py +++ b/pebble_tool/commands/sdk/project/__init__.py @@ -47,7 +47,7 @@ def __call__(self, args): event_map = { InvalidProjectException: "sdk_run_without_project", InvalidJSONException: "sdk_json_error", - OutdatedProjectException: "sdk_json_error", + OutdatedProjectException: "sdk_outdated_project", } if type(e) in event_map: post_event(event_map[type(e)]) From 251a8bcdf95783ae9c4f5b7c16d5987a78080d35 Mon Sep 17 00:00:00 2001 From: Cherie Williams Date: Thu, 8 Sep 2016 12:56:27 -0700 Subject: [PATCH 2/4] Add json validation with jsonschema --- pebble_tool/sdk/project.py | 106 ++++++++++++++++++++----------------- requirements.txt | 1 + 2 files changed, 57 insertions(+), 50 deletions(-) diff --git a/pebble_tool/sdk/project.py b/pebble_tool/sdk/project.py index 176fec2..6285be3 100644 --- a/pebble_tool/sdk/project.py +++ b/pebble_tool/sdk/project.py @@ -1,6 +1,9 @@ +from __future__ import print_function + __author__ = 'katharine' import json +from jsonschema import validate, ValidationError import os import os.path import uuid @@ -9,7 +12,7 @@ from pebble_tool.exceptions import (InvalidProjectException, InvalidJSONException, OutdatedProjectException, PebbleProjectException) -from pebble_tool.sdk import sdk_version +from pebble_tool.sdk import sdk_version, sdk_path from . import pebble_platforms @@ -59,35 +62,17 @@ def _parse_project(self): @staticmethod def check_project_directory(project_dir): - try: - NpmProject.check_project_directory(project_dir) - except PebbleProjectException: - AppinfoProject.check_project_directory(project_dir) + _validate_project_json(project_dir) + if not os.path.isdir(os.path.join(project_dir, 'src')): + raise InvalidProjectException("This is not a project directory.") + if not os.path.exists(os.path.join(project_dir, 'wscript')): + raise OutdatedProjectException("This project is missing a wscript file and cannot be handled by the SDK.") class AppinfoProject(PebbleProject): def __new__(cls, *args, **kwargs): return object.__new__(cls, *args, **kwargs) - @staticmethod - def check_project_directory(project_dir): - """Check to see if the current directory matches what is created by PblProjectCreator.run. - - Raises an InvalidProjectException or an OutdatedProjectException if everything isn't quite right. - """ - - if not os.path.isdir(os.path.join(project_dir, 'src')): - raise InvalidProjectException("This is not a project directory.") - - try: - with open(os.path.join(project_dir, "appinfo.json"), "r") as f: - try: - json.load(f) - except ValueError as e: - raise InvalidJSONException("Could not parse appinfo.json: %s" % e) - except IOError: - raise InvalidProjectException("Couldn't open project info.") - @staticmethod def should_process(project_dir): return os.path.exists(os.path.join(project_dir, 'appinfo.json')) @@ -120,36 +105,14 @@ class NpmProject(PebbleProject): def __new__(cls, *args, **kwargs): return object.__new__(cls, *args, **kwargs) - @staticmethod - def check_project_directory(project_dir): - """Check to see if the current directory matches what is created by PblProjectCreator.run. - - Raises an InvalidProjectException or an OutdatedProjectException if everything isn't quite right. - """ - - try: - with open(os.path.join(project_dir, "package.json"), "r") as f: - try: - app_info = json.load(f) - except ValueError as e: - raise InvalidJSONException("Could not parse package.json: %s" % ex) - if 'pebble' not in app_info: - raise InvalidProjectException("package.json doesn't have a 'pebble' key.") - except IOError: - if not os.path.isdir(os.path.join(project_dir, 'src')): - raise InvalidProjectException("This is not a project directory.") - raise InvalidProjectException("Couldn't open project info.") - @staticmethod def should_process(project_dir): + _validate_project_json(project_dir) package_json_path = os.path.join(project_dir, 'package.json') if os.path.exists(package_json_path): - with open(package_json_path) as f: - try: - if 'pebble' in json.load(f): - return True - except ValueError: - return False + with open(os.path.join(project_dir, 'package.json')) as f: + if 'pebble' in json.load(f): + return True return False def _parse_project(self): @@ -187,6 +150,49 @@ def _parse_project(self): self.long_name = self.short_name +def _validate_project_json(project_dir): + package_json_path = os.path.join(project_dir, 'package.json') + appinfo_json_path = os.path.join(project_dir, 'appinfo.json') + + try: + with open(package_json_path, 'r') as f: + try: + info = json.load(f) + except ValueError as e: + raise InvalidJSONException("package.json file does not contain valid JSON:\n{}".format(e)) + else: + if 'pebble' not in info: + raise IOError + except IOError: + try: + with open(appinfo_json_path, 'r') as f: + try: + info = json.load(f) + except ValueError as e: + raise InvalidJSONException("appinfo.json file does not contain valid JSON:\n{}".format(e)) + except IOError: + raise InvalidProjectException("Unable to find package.json file.") + else: + _validate_with_schema(info, "appinfo.json") + else: + _validate_with_schema(info, "package.json") + + +def _validate_with_schema(json_info, filetype): + try: + validate(json_info, _get_json_schema(filetype)) + except ValidationError as e: + raise InvalidJSONException(e) + + +def _get_json_schema(json_filetype): + schema_path = os.path.join(sdk_path(), 'pebble', 'common', 'tools', 'schemas', json_filetype) + if os.path.exists(schema_path): + with open(schema_path, "r") as f: + return json.load(f) + return {} + + def check_current_directory(): return PebbleProject.check_project_directory(os.getcwd()) diff --git a/requirements.txt b/requirements.txt index ea3b134..66c55af 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,3 +15,4 @@ websocket-client==0.32.0 wheel==0.24.0 colorama==0.3.3 packaging==16.7 +jsonschema==2.5.1 From 28d17048cb92d19c15a54a9b14e19e1cd574db86 Mon Sep 17 00:00:00 2001 From: Cherie Williams Date: Tue, 13 Sep 2016 13:32:53 -0700 Subject: [PATCH 3/4] Fix issue with relative ref paths --- pebble_tool/sdk/project.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pebble_tool/sdk/project.py b/pebble_tool/sdk/project.py index 6285be3..4d13520 100644 --- a/pebble_tool/sdk/project.py +++ b/pebble_tool/sdk/project.py @@ -3,7 +3,7 @@ __author__ = 'katharine' import json -from jsonschema import validate, ValidationError +from jsonschema import Draft4Validator, RefResolver, ValidationError import os import os.path import uuid @@ -180,17 +180,18 @@ def _validate_project_json(project_dir): def _validate_with_schema(json_info, filetype): try: - validate(json_info, _get_json_schema(filetype)) + _get_json_schema_validator(filetype).validate(json_info) except ValidationError as e: raise InvalidJSONException(e) -def _get_json_schema(json_filetype): +def _get_json_schema_validator(json_filetype): schema_path = os.path.join(sdk_path(), 'pebble', 'common', 'tools', 'schemas', json_filetype) if os.path.exists(schema_path): + resolver = RefResolver('file://' + os.path.dirname(schema_path) + '/', os.path.basename(schema_path)) with open(schema_path, "r") as f: - return json.load(f) - return {} + return Draft4Validator(json.load(f), resolver=resolver) + return Draft4Validator({}) def check_current_directory(): From 9ab5a2a67135bc21965bf2c11b54b4475642fb80 Mon Sep 17 00:00:00 2001 From: Cherie Williams Date: Tue, 13 Sep 2016 14:50:28 -0700 Subject: [PATCH 4/4] Provide better validation error reporting --- pebble_tool/sdk/project.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pebble_tool/sdk/project.py b/pebble_tool/sdk/project.py index 4d13520..2cd5c7b 100644 --- a/pebble_tool/sdk/project.py +++ b/pebble_tool/sdk/project.py @@ -182,7 +182,14 @@ def _validate_with_schema(json_info, filetype): try: _get_json_schema_validator(filetype).validate(json_info) except ValidationError as e: - raise InvalidJSONException(e) + error_message = "Unable to validate {} due to one of the following reasons:\n".format(filetype) + for error in [e] + e.context: + if error.absolute_path: + absolute_path = '.'.join([str(path) for path in list(error.absolute_path)]) + error_message += "- {}: {}\n".format(absolute_path, error.message) + else: + error_message += "- {}\n".format(error.message) + raise InvalidJSONException(error_message) def _get_json_schema_validator(json_filetype):