Skip to content

Commit afcd1e7

Browse files
committed
Merge remote-tracking branch 'origin/add-interactive-stack-trace' into add-interactive-stack-trace
2 parents 5a15963 + 30f83ab commit afcd1e7

18 files changed

+435
-82
lines changed

pip_version

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
3.4.3
1+
3.5.3
2+

pros.spec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ a = Analysis(
1515
['pros/cli/main.py'],
1616
pathex=[],
1717
binaries=[],
18-
datas=[],
18+
datas=[('pros/autocomplete/*', 'pros/autocomplete')],
1919
hiddenimports=[],
2020
hookspath=[],
2121
hooksconfig={},

pros/autocomplete/pros-complete.bash

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
_pros_completion() {
2+
local IFS=$'\n'
3+
local response
4+
response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD _PROS_COMPLETE=bash_complete $1)
5+
for completion in $response; do
6+
IFS=',' read type value <<<"$completion"
7+
if [[ $type == 'dir' ]]; then
8+
COMPREPLY=()
9+
compopt -o dirnames
10+
elif [[ $type == 'file' ]]; then
11+
COMPREPLY=()
12+
compopt -o default
13+
elif [[ $type == 'plain' ]]; then
14+
COMPREPLY+=($value)
15+
fi
16+
done
17+
return 0
18+
}
19+
_pros_completion_setup() {
20+
if [[ ${BASH_VERSINFO[0]} -ge 4 ]]; then
21+
complete -o nosort -F _pros_completion pros
22+
else
23+
complete -F _pros_completion pros
24+
fi
25+
}
26+
_pros_completion_setup

pros/autocomplete/pros-complete.ps1

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Modified from https://github.com/StephLin/click-pwsh/blob/main/click_pwsh/shell_completion.py#L11
2+
Register-ArgumentCompleter -Native -CommandName pros -ScriptBlock {
3+
param($wordToComplete, $commandAst, $cursorPosition)
4+
$env:COMP_WORDS = $commandAst
5+
$env:COMP_WORDS = $env:COMP_WORDS.replace('\\', '/')
6+
$incompleteCommand = $commandAst.ToString()
7+
$myCursorPosition = $cursorPosition
8+
if ($myCursorPosition -gt $incompleteCommand.Length) {
9+
$myCursorPosition = $incompleteCommand.Length
10+
}
11+
$env:COMP_CWORD = @($incompleteCommand.substring(0, $myCursorPosition).Split(" ") | Where-Object { $_ -ne "" }).Length
12+
if ( $wordToComplete.Length -gt 0) { $env:COMP_CWORD -= 1 }
13+
$env:_PROS_COMPLETE = "powershell_complete"
14+
pros | ForEach-Object {
15+
$type, $value, $help = $_.Split(",", 3)
16+
if ( ($type -eq "plain") -and ![string]::IsNullOrEmpty($value) ) {
17+
[System.Management.Automation.CompletionResult]::new($value, $value, "ParameterValue", $value)
18+
}
19+
elseif ( ($type -eq "file") -or ($type -eq "dir") ) {
20+
if ([string]::IsNullOrEmpty($wordToComplete)) {
21+
$dir = "./"
22+
}
23+
else {
24+
$dir = $wordToComplete.replace('\\', '/')
25+
}
26+
if ( (Test-Path -Path $dir) -and ((Get-Item $dir) -is [System.IO.DirectoryInfo]) ) {
27+
[System.Management.Automation.CompletionResult]::new($dir, $dir, "ParameterValue", $dir)
28+
}
29+
Get-ChildItem -Path $dir | Resolve-Path -Relative | ForEach-Object {
30+
$path = $_.ToString().replace('\\', '/').replace('Microsoft.PowerShell.Core/FileSystem::', '')
31+
$isDir = $false
32+
if ((Get-Item $path) -is [System.IO.DirectoryInfo]) {
33+
$path = $path + "/"
34+
$isDir = $true
35+
}
36+
if ( ($type -eq "file") -or ( ($type -eq "dir") -and $isDir ) ) {
37+
[System.Management.Automation.CompletionResult]::new($path, $path, "ParameterValue", $path)
38+
}
39+
}
40+
}
41+
}
42+
$env:COMP_WORDS = $null | Out-Null
43+
$env:COMP_CWORD = $null | Out-Null
44+
$env:_PROS_COMPLETE = $null | Out-Null
45+
}

pros/autocomplete/pros-complete.zsh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
_pros_completion() {
2+
local -a completions
3+
local -a completions_with_descriptions
4+
local -a response
5+
(( ! $+commands[pros] )) && return 1
6+
response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) _PROS_COMPLETE=zsh_complete pros)}")
7+
for type key descr in ${response}; do
8+
if [[ "$type" == "plain" ]]; then
9+
if [[ "$descr" == "_" ]]; then
10+
completions+=("$key")
11+
else
12+
completions_with_descriptions+=("$key":"$descr")
13+
fi
14+
elif [[ "$type" == "dir" ]]; then
15+
_path_files -/
16+
elif [[ "$type" == "file" ]]; then
17+
_path_files -f
18+
fi
19+
done
20+
if [ -n "$completions_with_descriptions" ]; then
21+
_describe -V unsorted completions_with_descriptions -U
22+
fi
23+
if [ -n "$completions" ]; then
24+
compadd -U -V unsorted -a completions
25+
fi
26+
}
27+
if [[ $zsh_eval_context[-1] == loadautofunc ]]; then
28+
_pros_completion "$@"
29+
else
30+
compdef _pros_completion pros
31+
fi

pros/autocomplete/pros.fish

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
function _pros_completion;
2+
set -l response (env _PROS_COMPLETE=fish_complete COMP_WORDS=(commandline -cp) COMP_CWORD=(commandline -t) pros);
3+
for completion in $response;
4+
set -l metadata (string split "," $completion);
5+
if test $metadata[1] = "dir";
6+
__fish_complete_directories $metadata[2];
7+
else if test $metadata[1] = "file";
8+
__fish_complete_path $metadata[2];
9+
else if test $metadata[1] = "plain";
10+
echo $metadata[2];
11+
end;
12+
end;
13+
end;
14+
complete --no-files --command pros --arguments "(_pros_completion)";

pros/cli/build.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import ctypes
2+
import sys
13
from typing import *
24

35
import click
@@ -24,6 +26,10 @@ def make(project: c.Project, build_args):
2426
analytics.send("make")
2527
exit_code = project.compile(build_args)
2628
if exit_code != 0:
29+
if sys.platform == 'win32':
30+
kernel32 = ctypes.windll.kernel32
31+
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
32+
2733
logger(__name__).error(f'Failed to make project: Exit Code {exit_code}', extra={'sentry': False})
2834
raise click.ClickException('Failed to build')
2935
return exit_code
@@ -71,6 +77,10 @@ def build_compile_commands(project: c.Project, suppress_output: bool, compile_co
7177
exit_code = project.make_scan_build(build_args, cdb_file=compile_commands, suppress_output=suppress_output,
7278
sandbox=sandbox)
7379
if exit_code != 0:
80+
if sys.platform == 'win32':
81+
kernel32 = ctypes.windll.kernel32
82+
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
83+
7484
logger(__name__).error(f'Failed to make project: Exit Code {exit_code}', extra={'sentry': False})
7585
raise click.ClickException('Failed to build')
7686
return exit_code

pros/cli/common.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def callback(ctx: click.Context, param: click.Parameter, value: bool):
124124
add_tag('no-sentry',value)
125125
if value:
126126
pros.common.sentry.disable_prompt()
127-
decorator = click.option('--no-sentry', expose_value=False, is_flag=True, default=False, is_eager=True,
127+
decorator = click.option('--no-sentry', expose_value=False, is_flag=True, default=True, is_eager=True,
128128
help="Disable sentry reporting prompt.", callback=callback, cls=PROSOption, hidden=True)(f)
129129
decorator.__name__ = f.__name__
130130
return decorator
@@ -191,10 +191,13 @@ def callback(ctx: click.Context, param: click.Parameter, value: str):
191191
if project_path is None:
192192
if allow_none:
193193
return None
194-
else:
194+
elif required:
195195
raise click.UsageError(f'{os.path.abspath(value or ".")} is not inside a PROS project. '
196196
f'Execute this command from within a PROS project or specify it '
197197
f'with --project project/path')
198+
else:
199+
return None
200+
198201
return c.Project(project_path)
199202

200203
def wrapper(f: Union[click.Command, Callable]):
@@ -253,11 +256,12 @@ def resolve_v5_port(port: Optional[str], type: str, quiet: bool = False) -> Tupl
253256
return None, False
254257
if len(ports) > 1:
255258
if not quiet:
256-
port = click.prompt('Multiple {} ports were found. Please choose one: [{}]'
257-
.format('v5', '|'.join([p.device for p in ports])),
258-
default=ports[0].device,
259+
brain_id = click.prompt('Multiple {} Brains were found. Please choose one to upload the program: [{}]'
260+
.format('v5', ' | '.join([p.product.split(' ')[-1] for p in ports])),
261+
default=ports[0].product.split(' ')[-1],
259262
show_default=False,
260-
type=click.Choice([p.device for p in ports]))
263+
type=click.Choice([p.description.split(' ')[-1] for p in ports]))
264+
port = [p.device for p in ports if p.description.split(' ')[-1] == brain_id][0]
261265
assert port in [p.device for p in ports]
262266
else:
263267
return None, False

pros/cli/conductor.py

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ def fetch(query: c.BaseTemplate):
6464
else:
6565
if template_file:
6666
logger(__name__).debug(f'Template file exists but is not a valid template: {template_file}')
67+
else:
68+
logger(__name__).error(f'Template not found: {query.name}')
69+
return -1
6770
template = c.Conductor().resolve_template(query, allow_offline=False)
6871
logger(__name__).debug(f'Template from resolved query: {template}')
6972
if template is None:
@@ -91,7 +94,7 @@ def fetch(query: c.BaseTemplate):
9194
help="Force apply the template, disregarding if the template is already installed.")
9295
@click.option('--remove-empty-dirs/--no-remove-empty-dirs', 'remove_empty_directories', is_flag=True, default=True,
9396
help='Remove empty directories when removing files')
94-
@click.option('--early-access/--disable-early-access', '--early/--disable-early', '-ea/-dea', 'early_access', '--beta/--disable-beta', default=None,
97+
@click.option('--early-access/--no-early-access', '--early/--no-early', '-ea/-nea', 'early_access', '--beta/--no-beta', default=None,
9598
help='Create a project using the PROS 4 kernel')
9699
@project_option()
97100
@template_query(required=True)
@@ -142,7 +145,7 @@ def install(ctx: click.Context, **kwargs):
142145
help="Force apply the template, disregarding if the template is already installed.")
143146
@click.option('--remove-empty-dirs/--no-remove-empty-dirs', 'remove_empty_directories', is_flag=True, default=True,
144147
help='Remove empty directories when removing files')
145-
@click.option('--early-access/--disable-early-access', '--early/--disable-early', '-ea/-dea', 'early_access', '--beta/--disable-beta', default=None,
148+
@click.option('--early-access/--no-early-access', '--early/--no-early', '-ea/-nea', 'early_access', '--beta/--no-beta', default=None,
146149
help='Create a project using the PROS 4 kernel')
147150
@project_option()
148151
@template_query(required=False)
@@ -156,7 +159,7 @@ def upgrade(ctx: click.Context, project: c.Project, query: c.BaseTemplate, **kwa
156159
"""
157160
analytics.send("upgrade-project")
158161
if not query.name:
159-
for template in project.templates.keys():
162+
for template in tuple(project.templates.keys()):
160163
click.secho(f'Upgrading {template}', color='yellow')
161164
q = c.BaseTemplate.create_query(name=template, target=project.target,
162165
supported_kernels=project.templates['kernel'].version)
@@ -204,7 +207,7 @@ def uninstall_template(project: c.Project, query: c.BaseTemplate, remove_user: b
204207
help='Compile the project after creation')
205208
@click.option('--build-cache', is_flag=True, default=None, show_default=False,
206209
help='Build compile commands cache after creation. Overrides --compile-after if both are specified.')
207-
@click.option('--early-access/--disable-early-access', '--early/--disable-early', '-ea/-dea', 'early_access', '--beta/--disable-beta', default=None,
210+
@click.option('--early-access/--no-early-access', '--early/--no-early', '-ea/-nea', 'early_access', '--beta/--no-beta', default=None,
208211
help='Create a project using the PROS 4 kernel')
209212
@click.pass_context
210213
@default_options
@@ -248,7 +251,7 @@ def new_project(ctx: click.Context, path: str, target: str, version: str,
248251

249252

250253
@conductor.command('query-templates',
251-
aliases=['search-templates', 'ls-templates', 'lstemplates', 'querytemplates', 'searchtemplates'],
254+
aliases=['search-templates', 'ls-templates', 'lstemplates', 'querytemplates', 'searchtemplates', 'q'],
252255
context_settings={'ignore_unknown_options': True})
253256
@click.option('--allow-offline/--no-offline', 'allow_offline', default=True, show_default=True,
254257
help='(Dis)allow offline templates in the listing')
@@ -258,12 +261,13 @@ def new_project(ctx: click.Context, path: str, target: str, version: str,
258261
help='Force update all remote depots, ignoring automatic update checks')
259262
@click.option('--limit', type=int, default=15,
260263
help='The maximum number of displayed results for each library')
261-
@click.option('--early-access/--disable-early-access', '--early/--disable-early', '-ea/-dea', 'early_access', '--beta/--disable-beta', default=None,
264+
@click.option('--early-access/--no-early-access', '--early/--no-early', '-ea/-nea', 'early_access', '--beta/--no-beta', default=None,
262265
help='View a list of early access templates')
263266
@template_query(required=False)
267+
@project_option(required=False)
264268
@click.pass_context
265269
@default_options
266-
def query_templates(ctx, query: c.BaseTemplate, allow_offline: bool, allow_online: bool, force_refresh: bool,
270+
def query_templates(ctx, project: Optional[c.Project], query: c.BaseTemplate, allow_offline: bool, allow_online: bool, force_refresh: bool,
267271
limit: int, early_access: bool):
268272
"""
269273
Query local and remote templates based on a spec
@@ -273,12 +277,10 @@ def query_templates(ctx, query: c.BaseTemplate, allow_offline: bool, allow_onlin
273277
analytics.send("query-templates")
274278
if limit < 0:
275279
limit = 15
280+
if early_access is None and project is not None:
281+
early_access = project.use_early_access
276282
templates = c.Conductor().resolve_templates(query, allow_offline=allow_offline, allow_online=allow_online,
277283
force_refresh=force_refresh, early_access=early_access)
278-
if early_access:
279-
templates += c.Conductor().resolve_templates(query, allow_offline=allow_offline, allow_online=allow_online,
280-
force_refresh=force_refresh, early_access=False)
281-
282284
render_templates = {}
283285
for template in templates:
284286
key = (template.identifier, template.origin)
@@ -368,4 +370,26 @@ def query_depots(url: bool):
368370
_conductor = c.Conductor()
369371
ui.echo(f"Available Depots{' (Add --url for the url)' if not url else ''}:\n")
370372
ui.echo('\n'.join(_conductor.query_depots(url))+"\n")
371-
373+
374+
@conductor.command('reset')
375+
@click.option('--force', is_flag=True, default=False, help='Force reset')
376+
@default_options
377+
def reset(force: bool):
378+
"""
379+
Reset conductor.pros
380+
381+
Visit https://pros.cs.purdue.edu/v5/cli/conductor.html to learn more
382+
"""
383+
384+
if not force:
385+
if not ui.confirm("This will remove all depots and templates. You will be unable to create a new PROS project if you do not have internet connection. Are you sure you want to continue?"):
386+
ui.echo("Aborting")
387+
return
388+
389+
# Delete conductor.pros
390+
file = os.path.join(click.get_app_dir('PROS'), 'conductor.pros')
391+
if os.path.exists(file):
392+
os.remove(file)
393+
394+
ui.echo("Conductor was reset")
395+

pros/cli/main.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import pros.cli.misc_commands
2828
import pros.cli.interactive
2929
import pros.cli.user_script
30+
import pros.conductor as c
3031

3132
if sys.platform == 'win32':
3233
kernel32 = ctypes.windll.kernel32
@@ -99,7 +100,23 @@ def use_analytics(ctx: click.Context, param, value):
99100
ctx.exit(0)
100101
ctx.ensure_object(dict)
101102
analytics.set_use(touse)
102-
ui.echo('Analytics set to : {}'.format(analytics.useAnalytics))
103+
ui.echo(f'Analytics usage set to: {analytics.useAnalytics}')
104+
ctx.exit(0)
105+
106+
def use_early_access(ctx: click.Context, param, value):
107+
if value is None:
108+
return
109+
conductor = c.Conductor()
110+
value = str(value).lower()
111+
if value.startswith("t") or value in ["1", "yes", "y"]:
112+
conductor.use_early_access = True
113+
elif value.startswith("f") or value in ["0", "no", "n"]:
114+
conductor.use_early_access = False
115+
else:
116+
ui.echo('Invalid argument provided for \'--use-early-access\'. Try \'--use-early-access=False\' or \'--use-early-access=True\'')
117+
ctx.exit(0)
118+
conductor.save()
119+
ui.echo(f'Early access set to: {conductor.use_early_access}')
103120
ctx.exit(0)
104121

105122

@@ -112,6 +129,8 @@ def use_analytics(ctx: click.Context, param, value):
112129
callback=version)
113130
@click.option('--use-analytics', help='Set analytics usage (True/False).', type=str, expose_value=False,
114131
is_eager=True, default=None, callback=use_analytics)
132+
@click.option('--use-early-access', type=str, expose_value=False, is_eager=True, default=None,
133+
help='Create projects with PROS 4 kernel by default', callback=use_early_access)
115134
def cli(ctx):
116135
pros.common.sentry.register()
117136
ctx.call_on_close(after_command)

0 commit comments

Comments
 (0)