Skip to content

Commit 0f45288

Browse files
author
Joseph Atkins-Turkish
committed
Rewrote find_project_root_and_manifest from scratch.
1 parent be3b1ca commit 0f45288

File tree

2 files changed

+36
-48
lines changed

2 files changed

+36
-48
lines changed

ide/tests/test_find_project_root.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def test_ignore_invalid_package_file(self):
9999
""" If a project has an invalid package.json and an appinfo.json, select the latter """
100100
self.run_test([
101101
FakeProjectItem("package.json", "{}"),
102-
"src/"
102+
"src/",
103103
"src/main.c",
104104
"appinfo.json"
105105
], "", "appinfo.json")
@@ -109,7 +109,7 @@ def test_PR_317(self):
109109
self.run_test([
110110
"MAINTAINERS",
111111
"package.json",
112-
"src/"
112+
"src/",
113113
"src/main.c",
114114
], "", "package.json")
115115

@@ -121,3 +121,9 @@ def test_find_most_shallow_project(self):
121121
"package.json",
122122
"src/main.c",
123123
], "", "package.json")
124+
125+
126+
def test_malformed_names_bug(self):
127+
""" Test for a bug where characters could be prepended to manifest names. """
128+
with self.assertRaises(InvalidProjectArchiveException):
129+
self.run_test(["project/rrrpackage.json", "project/rrrsrc/", "project/rrrsrc/main.c"])

ide/utils/project.py

+28-46
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import abc
22
import json
3-
import os.path
3+
import os
44

55
from django.utils.translation import ugettext as _
66

77
__author__ = 'katharine'
88

9-
9+
SRC_DIR = 'src'
1010
PACKAGE_MANIFEST = 'package.json'
1111
APPINFO_MANIFEST = 'appinfo.json'
1212
MANIFEST_KINDS = [PACKAGE_MANIFEST, APPINFO_MANIFEST]
@@ -33,57 +33,39 @@ def path(self):
3333
return None
3434

3535

36-
def is_manifest(kind, contents):
37-
""" A potentially valid manifest is a package.json file with a "pebble" object, or an appinfo.json file. """
38-
if kind == PACKAGE_MANIFEST:
39-
return 'pebble' in json.loads(contents)
40-
elif kind == APPINFO_MANIFEST:
41-
return True
42-
else:
43-
return False
36+
def rank_manifest_path(dirname, kind):
37+
""" Sort key for manifest files. Sort first by depth and add a penalty for being an appinfo.json """
38+
return os.path.normpath(dirname).count('/') + (0.5 if kind == APPINFO_MANIFEST else 0)
4439

4540

4641
def find_project_root_and_manifest(project_items):
4742
""" Given the contents of an archive, find a valid Pebble project.
4843
:param project_items: A list of BaseProjectItems
4944
:return: A tuple of (path_to_project, manifest BaseProjectItem)
5045
"""
51-
SRC_DIR = 'src/'
52-
53-
# Sort the paths by the number of path separators they have,
54-
sorted_items = sorted(project_items, key=lambda x: os.path.normpath(x.path).count('/'))
55-
for i, item in enumerate(sorted_items):
56-
base_dir = item.path
57-
58-
# Check if the file is one of the kinds of manifest file
59-
for name in MANIFEST_KINDS:
60-
dir_end = base_dir.rfind(name)
61-
if dir_end == -1:
62-
continue
63-
# Ensure that the file is actually a manifest file
64-
if dir_end + len(name) == len(base_dir):
65-
if is_manifest(name, item.read()):
66-
manifest_item = item
67-
break
68-
else:
69-
# If the file is not a manifest file, continue looking for the manfiest.
70-
continue
46+
found_manifests = set()
47+
found_src_directories = set()
7148

72-
# The base dir is the location of the manifest file without the manifest filename.
73-
base_dir = base_dir[:dir_end]
74-
75-
# Now check the rest of the items for a source directory containing at least one source file.
76-
for source_item in sorted_items[i+1:]:
77-
source_dir = source_item.path
78-
if source_dir[:dir_end] != base_dir:
79-
continue
80-
if not source_dir.endswith('.c') and not source_dir.endswith('.js'):
81-
continue
82-
if source_dir[dir_end:dir_end + len(SRC_DIR)] != SRC_DIR:
83-
continue
84-
break
85-
else:
86-
# If there was no source directory with a source file, keep looking for manifest files.
49+
for item in project_items:
50+
item_path = item.path
51+
52+
# If the item looks like a manifest, add it to a set of potential manifests
53+
item_dirname, item_basename = os.path.split(item_path)
54+
if (item_basename == PACKAGE_MANIFEST and 'pebble' in json.loads(item.read())) or item_basename == APPINFO_MANIFEST:
55+
found_manifests.add(((item_dirname, item_basename), item))
8756
continue
88-
return base_dir, manifest_item
57+
58+
# Otherwise, check if the file is a source file
59+
if item_path.endswith(('.c', '.js')):
60+
# If it is a source file in an 'src' directory, add its parent to the set of potential project directories
61+
source_dir_dirname, source_dir_basename = os.path.split(item_dirname)
62+
if source_dir_basename == SRC_DIR:
63+
found_src_directories.add(source_dir_dirname)
64+
65+
# Choose the most shallow manifest file which has a non-empty source directory.
66+
sorted_manifests = sorted(found_manifests, key=lambda x: rank_manifest_path(*x[0]))
67+
for (base, kind), item in sorted_manifests:
68+
if base in found_src_directories:
69+
base = base + os.sep if base else ""
70+
return base + os.sep, item
8971
raise InvalidProjectArchiveException(_("No project root found."))

0 commit comments

Comments
 (0)