Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop #634

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
629269d
test change
xyang-github Oct 26, 2021
b5747a7
edited the regex for make_app_name to remove leading digits from the …
xyang-github Oct 26, 2021
171dada
Changed PEP508 regex in command.py to prevent names starting with a n…
xyang-github Oct 26, 2021
4bab13a
Changed class name to be derived from app name instead of formal name…
xyang-github Oct 26, 2021
d312797
Made changes to the make_default_app_name function by adding a condit…
xyang-github Oct 26, 2021
12339f9
Changed how class_name is derived. Now ask user for a class name, and…
xyang-github Oct 28, 2021
3865150
Removed pep508 regex from config.py as it isn't sufficient for the re…
xyang-github Oct 28, 2021
f01ac41
Remoed import statement for pep508 regex
xyang-github Oct 28, 2021
11c3b47
Added back the pep508 regex in the config.py file, as this is require…
xyang-github Oct 28, 2021
ddad887
- removed an entry from the valid testing that had a leading digit
xyang-github Oct 28, 2021
2e9df93
Changed wording of the suggested app_name if the formal_name containe…
xyang-github Oct 28, 2021
d535e19
Added a make_class_name so that there is a default value that can be …
xyang-github Oct 28, 2021
93d9598
Added function to convert default_class_name to title case.
xyang-github Oct 28, 2021
6a1e25a
Included test for class name now that it is being explicitly asked. T…
xyang-github Oct 28, 2021
e91d9b3
Changed the final test candidate to remove leading digit. Adding an i…
xyang-github Oct 28, 2021
2857cff
Added a test page for valid_class_name. Uses a lot of the criteria in…
xyang-github Oct 28, 2021
a4e3370
Allow non-leading underscores in the class name.
xyang-github Oct 28, 2021
120d2e5
Cleaned up some code.
xyang-github Oct 29, 2021
f6ed116
Renamed test file name
xyang-github Oct 29, 2021
91d0d72
Changed class_name result to reflect camel case (My Application --> M…
xyang-github Oct 29, 2021
aa348c9
Added a test for a formal name that starts with a digit.
xyang-github Oct 29, 2021
2457ae6
Added a test that uses underscores in the beginning and end of the fo…
xyang-github Oct 29, 2021
88451f9
Added tests that includes hyphens.
xyang-github Oct 29, 2021
3cb5838
Changed some formatting, and also the text for the value error when v…
xyang-github Oct 29, 2021
5256f75
Updated the pep508_name_re in the config file instead of introducing …
xyang-github Nov 1, 2021
9b7105b
the title() method for the formal_name was combined in the return sta…
xyang-github Nov 1, 2021
8ba62d7
added a test where the formal names are written in lower case letters…
xyang-github Nov 1, 2021
6dfb30d
Corrected spelling mistake.
xyang-github Nov 1, 2021
cf86ed9
Added a test to include non-latin characters only, and a combination …
xyang-github Nov 1, 2021
3626b52
Removed the 'ignore cases' since it is important that app name is in …
xyang-github Nov 1, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
63 changes: 43 additions & 20 deletions src/briefcase/commands/new.py
Expand Up @@ -7,7 +7,7 @@

from cookiecutter import exceptions as cookiecutter_exceptions

from briefcase.config import PEP508_NAME_RE

from briefcase.exceptions import NetworkFailure

from .base import BaseCommand, BriefcaseCommandError
Expand Down Expand Up @@ -74,26 +74,14 @@ def add_options(self, parser):
help='The cookiecutter template to use for the new project'
)

def make_class_name(self, formal_name):
"""
Construct a valid class name from a formal name.

:param formal_name: The formal name
:returns: The app's class name
"""
class_name = re.sub('[^0-9a-zA-Z_]+', '', formal_name)
if class_name[0].isdigit():
class_name = '_' + class_name
return class_name

def make_app_name(self, formal_name):
"""
Construct a candidate app name from a formal name.

:param formal_name: The formal name
:returns: The candidate app name
"""
return re.sub('[^0-9a-zA-Z_]+', '', formal_name).lstrip('_').lower()
return re.sub('[^a-zA-Z]*[^a-zA-Z0-9_]+', '', formal_name).lstrip('_').rstrip('_').lower()

def validate_app_name(self, candidate):
"""
Expand All @@ -103,10 +91,11 @@ def validate_app_name(self, candidate):
:returns: True. If there are any validation problems, raises ValueError
with a diagnostic message.
"""
if not PEP508_NAME_RE.match(candidate):
if not re.match('^[a-z][a-zA-Z0-9._-]*[a-zA-Z0-9]$', candidate):
xyang-github marked this conversation as resolved.
Show resolved Hide resolved
raise ValueError(
"App name may only contain letters, numbers, hypens and "
"underscores, and may not start with a hyphen or underscore."
"App names must be PEP508 compliant (i.e., they can only "
"include letters, numbers, '-' and '_'; must start with a "
"letter; and cannot end with '-' or '_'."
)
if (self.base_path / candidate).exists():
raise ValueError(
Expand All @@ -117,6 +106,33 @@ def validate_app_name(self, candidate):

return True

def make_class_name(self, formal_name):
"""
Construct a candidate class name from a formal name.

:param formal_name: The formal name
:returns: The candidate app name; will appear blank if only non-latin characters
were entered in the formal_name
"""
formal_name.title()
xyang-github marked this conversation as resolved.
Show resolved Hide resolved
return re.sub('[^a-zA-Z]*[^a-zA-Z0-9_]+', '', formal_name)

def validate_class_name(self, candidate):
"""
Determine if class name is valid

:param candidate: The candidate name
:returns: True. If there are any validation problems, raises ValueError
with a diagnostic message.
"""
if not re.match('^[A-Z]+[a-zA-Z0-9_]+$', candidate):
raise ValueError(
"The class name must start with a capital letter in the CapWords format."
xyang-github marked this conversation as resolved.
Show resolved Hide resolved
"It should not contain spaces."
)

return True

def make_module_name(self, app_name):
"""
Construct a valid module name from an app name.
Expand Down Expand Up @@ -317,9 +333,6 @@ def build_app_context(self):
default='Hello World',
)

# The class name can be completely derived from the formal name.
class_name = self.make_class_name(formal_name)

default_app_name = self.make_app_name(formal_name)
app_name = self.input_text(
intro="""
Expand All @@ -337,6 +350,16 @@ def build_app_context(self):
validator=self.validate_app_name,
)

default_class_name = self.make_class_name(formal_name)
class_name = self.input_text(
intro="""
Next, we need a name that will serve as the App class name. The class name must start with
a capital letter in the CapWords format. It should not contain spaces.""",
variable="class name",
default=default_class_name,
validator=self.validate_class_name,
)

# The module name can be completely derived from the app name.
module_name = self.make_module_name(app_name)

Expand Down
6 changes: 4 additions & 2 deletions tests/commands/new/test_build_app_context.py
Expand Up @@ -6,6 +6,7 @@ def test_question_sequence(new_command):
new_command.input.values = [
'My Application', # formal name
'', # app name - accept the default
'', # class name
'org.beeware', # bundle ID
'My Project', # project name
'Cool stuff', # description
Expand All @@ -18,8 +19,8 @@ def test_question_sequence(new_command):

assert new_command.build_app_context() == {
'formal_name': 'My Application',
'class_name': 'MyApplication',
'app_name': 'myapplication',
'class_name': 'MyApplication',
'module_name': 'myapplication',
'bundle': 'org.beeware',
'project_name': 'My Project',
Expand All @@ -39,6 +40,7 @@ def test_question_sequence_with_nondefault_gui(new_command):
new_command.input.values = [
'My Application', # formal name
'', # app name - accept the default
'', # class name
'org.beeware', # bundle ID
'My Project', # project name
'Cool stuff', # description
Expand All @@ -51,8 +53,8 @@ def test_question_sequence_with_nondefault_gui(new_command):

assert new_command.build_app_context() == {
'formal_name': 'My Application',
'class_name': 'MyApplication',
'app_name': 'myapplication',
'class_name': 'MyApplication',
'module_name': 'myapplication',
'bundle': 'org.beeware',
'project_name': 'My Project',
Expand Down
6 changes: 6 additions & 0 deletions tests/commands/new/test_make_app_name.py
Expand Up @@ -8,6 +8,12 @@
('Hello World!', 'helloworld'),
('Hello! World', 'helloworld'),
('Hello-World', 'helloworld'),
('98 Hello World', 'helloworld'),
('Hello World_', 'helloworld'),
('Hello world.', 'helloworld'),
('_HelloWorld_', 'helloworld'),
('Hello_World_', 'hello_world'),
('Hello World-', 'helloworld'),
]
)
def test_make_app_name(new_command, formal_name, candidate):
Expand Down
2 changes: 1 addition & 1 deletion tests/commands/new/test_make_class_name.py
Expand Up @@ -9,7 +9,7 @@
('Hello! World', 'HelloWorld'),
('Hello_World', 'Hello_World'),
('Hello-World', 'HelloWorld'),
('24 Jump Street', '_24JumpStreet'),
xyang-github marked this conversation as resolved.
Show resolved Hide resolved
('24 Jump Street', 'JumpStreet'),
]
)
def test_make_class_name(new_command, formal_name, candidate):
Expand Down
5 changes: 4 additions & 1 deletion tests/commands/new/test_validate_app_name.py
Expand Up @@ -7,7 +7,6 @@
'helloworld',
'helloWorld',
'hello42world',
'42helloworld', # ?? Are we sure this is correct?
'hello_world',
'hello-world',
]
Expand All @@ -25,6 +24,10 @@ def test_valid_app_name(new_command, name):
'_helloworld', # leading underscore
'-helloworld', # leading hyphen
'existing', # pre-existing directory
'98helloworld', # leading digit
'', # blank
'学口算', # non-latin character
'Helloworld' # start with capital letter
]
)
def test_invalid_app_name(new_command, name, tmp_path):
Expand Down
36 changes: 36 additions & 0 deletions tests/commands/new/test_validate_class_name.py
@@ -0,0 +1,36 @@
import pytest


@pytest.mark.parametrize(
'name',
[
'HelloWorld',
'HelloWorld98',
'Hello42World',
'Hello_World',
]
)
def test_valid_class_name(new_command, name):
"Test that valid app names are accepted"
assert new_command.validate_class_name(name)


@pytest.mark.parametrize(
'name',
[
'hello world', # space
'helloworld!', # punctuation
'_helloworld', # leading underscore
'-helloworld', # leading hyphen
'98helloworld', # leading digit
'', # blank
'学口算', # non-latin character
'helloworld', # no capitalized letter
]
)
def test_invalid_class_name(new_command, name, tmp_path):
"Test that invalid app names are rejected"
(tmp_path / 'existing').mkdir()

with pytest.raises(ValueError):
new_command.validate_class_name(name)