canvaslms 5.10__py3-none-any.whl → 6.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- canvaslms/cli/__init__.py +13 -1
- canvaslms/cli/assignments.nw +151 -49
- canvaslms/cli/assignments.py +249 -84
- canvaslms/cli/cli.nw +16 -1
- canvaslms/cli/content.nw +290 -14
- canvaslms/cli/content.py +93 -8
- canvaslms/cli/pages.nw +130 -77
- canvaslms/cli/pages.py +86 -69
- canvaslms/cli/quizzes.nw +100 -14
- canvaslms/cli/quizzes.py +103 -13
- {canvaslms-5.10.dist-info → canvaslms-6.1.dist-info}/METADATA +1 -1
- {canvaslms-5.10.dist-info → canvaslms-6.1.dist-info}/RECORD +15 -15
- {canvaslms-5.10.dist-info → canvaslms-6.1.dist-info}/WHEEL +0 -0
- {canvaslms-5.10.dist-info → canvaslms-6.1.dist-info}/entry_points.txt +0 -0
- {canvaslms-5.10.dist-info → canvaslms-6.1.dist-info}/licenses/LICENSE +0 -0
canvaslms/cli/assignments.py
CHANGED
|
@@ -289,6 +289,12 @@ def assignments_view_command(config, canvas, args):
|
|
|
289
289
|
assignment_rubric = None
|
|
290
290
|
if hasattr(assignment, "rubric") and assignment.rubric:
|
|
291
291
|
assignment_rubric = assignment.rubric
|
|
292
|
+
if hasattr(args, "assignment") and args.assignment not in (None, ".*"):
|
|
293
|
+
# User specified a -a filter, use it verbatim
|
|
294
|
+
assignment_regex = args.assignment
|
|
295
|
+
else:
|
|
296
|
+
# Generate exact-match regex from name
|
|
297
|
+
assignment_regex = "^" + re.escape(assignment.name) + "$"
|
|
292
298
|
output = canvaslms.cli.content.render_to_html(
|
|
293
299
|
assignment,
|
|
294
300
|
canvaslms.cli.content.ASSIGNMENT_SCHEMA,
|
|
@@ -296,6 +302,7 @@ def assignments_view_command(config, canvas, args):
|
|
|
296
302
|
extra_attributes={
|
|
297
303
|
"modules": assignment_modules,
|
|
298
304
|
"rubric": assignment_rubric,
|
|
305
|
+
"regex": assignment_regex,
|
|
299
306
|
},
|
|
300
307
|
)
|
|
301
308
|
print(output)
|
|
@@ -306,6 +313,12 @@ def assignments_view_command(config, canvas, args):
|
|
|
306
313
|
assignment_rubric = None
|
|
307
314
|
if hasattr(assignment, "rubric") and assignment.rubric:
|
|
308
315
|
assignment_rubric = assignment.rubric
|
|
316
|
+
if hasattr(args, "assignment") and args.assignment not in (None, ".*"):
|
|
317
|
+
# User specified a -a filter, use it verbatim
|
|
318
|
+
assignment_regex = args.assignment
|
|
319
|
+
else:
|
|
320
|
+
# Generate exact-match regex from name
|
|
321
|
+
assignment_regex = "^" + re.escape(assignment.name) + "$"
|
|
309
322
|
output = canvaslms.cli.content.render_to_markdown(
|
|
310
323
|
assignment,
|
|
311
324
|
canvaslms.cli.content.ASSIGNMENT_SCHEMA,
|
|
@@ -313,6 +326,7 @@ def assignments_view_command(config, canvas, args):
|
|
|
313
326
|
extra_attributes={
|
|
314
327
|
"modules": assignment_modules,
|
|
315
328
|
"rubric": assignment_rubric,
|
|
329
|
+
"regex": assignment_regex,
|
|
316
330
|
},
|
|
317
331
|
)
|
|
318
332
|
print(output)
|
|
@@ -502,37 +516,42 @@ def assignments_edit_command(config, canvas, args):
|
|
|
502
516
|
args.file
|
|
503
517
|
)
|
|
504
518
|
except FileNotFoundError:
|
|
505
|
-
|
|
506
|
-
print(f"Error: File not found: {args.file}", file=sys.stderr)
|
|
507
|
-
return
|
|
519
|
+
canvaslms.cli.err(1, f"File not found: {args.file}")
|
|
508
520
|
except Exception as e:
|
|
509
|
-
|
|
510
|
-
print(f"Error reading file: {e}", file=sys.stderr)
|
|
511
|
-
return
|
|
521
|
+
canvaslms.cli.err(1, f"Error reading file: {e}")
|
|
512
522
|
|
|
513
523
|
errors = canvaslms.cli.content.validate_attributes(
|
|
514
524
|
attributes, canvaslms.cli.content.ASSIGNMENT_SCHEMA
|
|
515
525
|
)
|
|
516
526
|
if errors:
|
|
517
527
|
for error in errors:
|
|
518
|
-
|
|
519
|
-
|
|
528
|
+
logger.error(f"Validation error: {error}")
|
|
529
|
+
canvaslms.cli.err(1, "File validation failed")
|
|
520
530
|
if args.html:
|
|
521
531
|
html_content = body_content # Already HTML, no conversion needed
|
|
522
532
|
else:
|
|
523
533
|
html_content = pypandoc.convert_text(body_content, "html", format="md")
|
|
524
534
|
|
|
525
|
-
if "
|
|
526
|
-
|
|
535
|
+
if "regex" in attributes and attributes["regex"]:
|
|
536
|
+
regex_pattern = attributes["regex"]
|
|
527
537
|
course_list = courses.process_course_option(canvas, args)
|
|
528
538
|
if not course_list:
|
|
529
|
-
|
|
530
|
-
return
|
|
539
|
+
canvaslms.cli.err(1, "No courses found matching criteria")
|
|
531
540
|
|
|
532
541
|
course = course_list[0]
|
|
542
|
+
|
|
543
|
+
all_assignments = list(course.get_assignments())
|
|
544
|
+
for a in all_assignments:
|
|
545
|
+
a.course = course
|
|
546
|
+
|
|
533
547
|
try:
|
|
534
|
-
assignment =
|
|
535
|
-
|
|
548
|
+
assignment = canvaslms.cli.content.match_item_by_regex(
|
|
549
|
+
all_assignments,
|
|
550
|
+
regex_pattern,
|
|
551
|
+
[lambda a: a.name, lambda a: str(a.id)],
|
|
552
|
+
item_type="assignment",
|
|
553
|
+
name_func=lambda a: a.name,
|
|
554
|
+
)
|
|
536
555
|
update_data = {
|
|
537
556
|
"assignment": {
|
|
538
557
|
"name": attributes.get("name", assignment.name),
|
|
@@ -584,12 +603,12 @@ def assignments_edit_command(config, canvas, args):
|
|
|
584
603
|
file=sys.stderr,
|
|
585
604
|
)
|
|
586
605
|
except Exception as e:
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
f"Error updating assignment '{assignment.name}': {e}",
|
|
590
|
-
file=sys.stderr,
|
|
606
|
+
canvaslms.cli.err(
|
|
607
|
+
1, f"Error updating assignment '{assignment.name}': {e}"
|
|
591
608
|
)
|
|
592
|
-
except
|
|
609
|
+
except re.error as e:
|
|
610
|
+
canvaslms.cli.err(1, f"Invalid regex pattern '{regex_pattern}': {e}")
|
|
611
|
+
except canvaslms.cli.EmptyListError:
|
|
593
612
|
if args.create:
|
|
594
613
|
create_params = {
|
|
595
614
|
"name": attributes.get("name", "Untitled Assignment"),
|
|
@@ -617,9 +636,9 @@ def assignments_edit_command(config, canvas, args):
|
|
|
617
636
|
assignment=create_params
|
|
618
637
|
)
|
|
619
638
|
new_assignment.course = course
|
|
620
|
-
|
|
621
|
-
f"Created assignment: {new_assignment.name}
|
|
622
|
-
|
|
639
|
+
logger.info(
|
|
640
|
+
f"Created assignment: {new_assignment.name} "
|
|
641
|
+
f"(id: {new_assignment.id})"
|
|
623
642
|
)
|
|
624
643
|
if "modules" in attributes:
|
|
625
644
|
module_regexes = attributes["modules"]
|
|
@@ -635,75 +654,211 @@ def assignments_edit_command(config, canvas, args):
|
|
|
635
654
|
file=sys.stderr,
|
|
636
655
|
)
|
|
637
656
|
except Exception as e:
|
|
638
|
-
|
|
639
|
-
print(f"Error creating assignment: {e}", file=sys.stderr)
|
|
657
|
+
canvaslms.cli.err(1, f"Error creating assignment: {e}")
|
|
640
658
|
else:
|
|
641
|
-
|
|
642
|
-
|
|
659
|
+
canvaslms.cli.err(
|
|
660
|
+
1,
|
|
661
|
+
f"No assignments match regex '{regex_pattern}'. "
|
|
643
662
|
f"Use --create to create a new assignment.",
|
|
644
|
-
file=sys.stderr,
|
|
645
663
|
)
|
|
646
|
-
return
|
|
647
664
|
else:
|
|
648
|
-
|
|
665
|
+
if "name" in attributes and attributes["name"]:
|
|
666
|
+
name = attributes["name"]
|
|
667
|
+
course_list = courses.process_course_option(canvas, args)
|
|
668
|
+
if not course_list:
|
|
669
|
+
canvaslms.cli.err(1, "No courses found matching criteria")
|
|
649
670
|
|
|
650
|
-
|
|
651
|
-
update_data = {
|
|
652
|
-
"assignment": {
|
|
653
|
-
"name": attributes.get("name", assignment.name),
|
|
654
|
-
"description": html_content,
|
|
655
|
-
}
|
|
656
|
-
}
|
|
671
|
+
course = course_list[0]
|
|
657
672
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
update_data["assignment"]["unlock_at"] = attributes["unlock_at"]
|
|
662
|
-
if "lock_at" in attributes and attributes["lock_at"] is not None:
|
|
663
|
-
update_data["assignment"]["lock_at"] = attributes["lock_at"]
|
|
664
|
-
if (
|
|
665
|
-
"points_possible" in attributes
|
|
666
|
-
and attributes["points_possible"] is not None
|
|
667
|
-
):
|
|
668
|
-
update_data["assignment"]["points_possible"] = attributes[
|
|
669
|
-
"points_possible"
|
|
670
|
-
]
|
|
671
|
-
if "published" in attributes:
|
|
672
|
-
update_data["assignment"]["published"] = attributes["published"]
|
|
673
|
+
all_assignments = list(course.get_assignments())
|
|
674
|
+
for a in all_assignments:
|
|
675
|
+
a.course = course
|
|
673
676
|
|
|
674
677
|
try:
|
|
675
|
-
assignment.
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
678
|
+
assignment = canvaslms.cli.content.match_item_by_name(
|
|
679
|
+
all_assignments, name, lambda a: a.name, item_type="assignment"
|
|
680
|
+
)
|
|
681
|
+
update_data = {
|
|
682
|
+
"assignment": {
|
|
683
|
+
"name": attributes.get("name", assignment.name),
|
|
684
|
+
"description": html_content,
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
if "due_at" in attributes and attributes["due_at"] is not None:
|
|
689
|
+
update_data["assignment"]["due_at"] = attributes["due_at"]
|
|
690
|
+
if (
|
|
691
|
+
"unlock_at" in attributes
|
|
692
|
+
and attributes["unlock_at"] is not None
|
|
693
|
+
):
|
|
694
|
+
update_data["assignment"]["unlock_at"] = attributes["unlock_at"]
|
|
695
|
+
if "lock_at" in attributes and attributes["lock_at"] is not None:
|
|
696
|
+
update_data["assignment"]["lock_at"] = attributes["lock_at"]
|
|
697
|
+
if (
|
|
698
|
+
"points_possible" in attributes
|
|
699
|
+
and attributes["points_possible"] is not None
|
|
700
|
+
):
|
|
701
|
+
update_data["assignment"]["points_possible"] = attributes[
|
|
702
|
+
"points_possible"
|
|
703
|
+
]
|
|
704
|
+
if "published" in attributes:
|
|
705
|
+
update_data["assignment"]["published"] = attributes["published"]
|
|
706
|
+
|
|
707
|
+
try:
|
|
708
|
+
assignment.edit(**update_data)
|
|
709
|
+
assignment._fetched_at = datetime.now()
|
|
710
|
+
if hasattr(assignment.course, "assignment_cache"):
|
|
711
|
+
assignment.course.assignment_cache[assignment.id] = (
|
|
712
|
+
assignment,
|
|
713
|
+
{},
|
|
714
|
+
)
|
|
715
|
+
logger.info(f"Updated assignment: {assignment.name}")
|
|
716
|
+
if "modules" in attributes:
|
|
717
|
+
module_regexes = attributes["modules"]
|
|
718
|
+
added, removed = modules.update_item_modules(
|
|
719
|
+
assignment.course,
|
|
720
|
+
"Assignment",
|
|
721
|
+
assignment.id,
|
|
722
|
+
module_regexes,
|
|
723
|
+
)
|
|
724
|
+
if added:
|
|
725
|
+
print(
|
|
726
|
+
f" Added to modules: {', '.join(added)}",
|
|
727
|
+
file=sys.stderr,
|
|
728
|
+
)
|
|
729
|
+
if removed:
|
|
730
|
+
print(
|
|
731
|
+
f" Removed from modules: {', '.join(removed)}",
|
|
732
|
+
file=sys.stderr,
|
|
733
|
+
)
|
|
734
|
+
except Exception as e:
|
|
735
|
+
canvaslms.cli.err(
|
|
736
|
+
1, f"Error updating assignment '{assignment.name}': {e}"
|
|
690
737
|
)
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
738
|
+
except canvaslms.cli.EmptyListError:
|
|
739
|
+
if args.create:
|
|
740
|
+
create_params = {
|
|
741
|
+
"name": attributes.get("name", "Untitled Assignment"),
|
|
742
|
+
"description": html_content,
|
|
743
|
+
}
|
|
744
|
+
if "due_at" in attributes and attributes["due_at"] is not None:
|
|
745
|
+
create_params["due_at"] = attributes["due_at"]
|
|
746
|
+
if (
|
|
747
|
+
"unlock_at" in attributes
|
|
748
|
+
and attributes["unlock_at"] is not None
|
|
749
|
+
):
|
|
750
|
+
create_params["unlock_at"] = attributes["unlock_at"]
|
|
751
|
+
if (
|
|
752
|
+
"lock_at" in attributes
|
|
753
|
+
and attributes["lock_at"] is not None
|
|
754
|
+
):
|
|
755
|
+
create_params["lock_at"] = attributes["lock_at"]
|
|
756
|
+
if (
|
|
757
|
+
"points_possible" in attributes
|
|
758
|
+
and attributes["points_possible"] is not None
|
|
759
|
+
):
|
|
760
|
+
create_params["points_possible"] = attributes[
|
|
761
|
+
"points_possible"
|
|
762
|
+
]
|
|
763
|
+
if "published" in attributes:
|
|
764
|
+
create_params["published"] = attributes["published"]
|
|
765
|
+
|
|
766
|
+
try:
|
|
767
|
+
new_assignment = course.create_assignment(
|
|
768
|
+
assignment=create_params
|
|
695
769
|
)
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
f"
|
|
699
|
-
|
|
770
|
+
new_assignment.course = course
|
|
771
|
+
logger.info(
|
|
772
|
+
f"Created assignment: {new_assignment.name} "
|
|
773
|
+
f"(id: {new_assignment.id})"
|
|
700
774
|
)
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
775
|
+
if "modules" in attributes:
|
|
776
|
+
module_regexes = attributes["modules"]
|
|
777
|
+
added, removed = modules.update_item_modules(
|
|
778
|
+
new_assignment.course,
|
|
779
|
+
"Assignment",
|
|
780
|
+
new_assignment.id,
|
|
781
|
+
module_regexes,
|
|
782
|
+
)
|
|
783
|
+
if added:
|
|
784
|
+
print(
|
|
785
|
+
f" Added to modules: {', '.join(added)}",
|
|
786
|
+
file=sys.stderr,
|
|
787
|
+
)
|
|
788
|
+
except Exception as e:
|
|
789
|
+
canvaslms.cli.err(1, f"Error creating assignment: {e}")
|
|
790
|
+
else:
|
|
791
|
+
canvaslms.cli.err(
|
|
792
|
+
1,
|
|
793
|
+
f"No assignments match name '{name}'. "
|
|
794
|
+
f"Use --create to create a new assignment.",
|
|
795
|
+
)
|
|
796
|
+
else:
|
|
797
|
+
assignment_list = process_assignment_option(canvas, args)
|
|
798
|
+
if len(assignment_list) > 1:
|
|
799
|
+
canvaslms.cli.err(
|
|
800
|
+
1,
|
|
801
|
+
f"{len(assignment_list)} assignments match filter. "
|
|
802
|
+
f"Add 'name' or 'regex' to YAML, or use -a to narrow selection.",
|
|
706
803
|
)
|
|
804
|
+
for assignment in assignment_list:
|
|
805
|
+
update_data = {
|
|
806
|
+
"assignment": {
|
|
807
|
+
"name": attributes.get("name", assignment.name),
|
|
808
|
+
"description": html_content,
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
if "due_at" in attributes and attributes["due_at"] is not None:
|
|
813
|
+
update_data["assignment"]["due_at"] = attributes["due_at"]
|
|
814
|
+
if (
|
|
815
|
+
"unlock_at" in attributes
|
|
816
|
+
and attributes["unlock_at"] is not None
|
|
817
|
+
):
|
|
818
|
+
update_data["assignment"]["unlock_at"] = attributes["unlock_at"]
|
|
819
|
+
if "lock_at" in attributes and attributes["lock_at"] is not None:
|
|
820
|
+
update_data["assignment"]["lock_at"] = attributes["lock_at"]
|
|
821
|
+
if (
|
|
822
|
+
"points_possible" in attributes
|
|
823
|
+
and attributes["points_possible"] is not None
|
|
824
|
+
):
|
|
825
|
+
update_data["assignment"]["points_possible"] = attributes[
|
|
826
|
+
"points_possible"
|
|
827
|
+
]
|
|
828
|
+
if "published" in attributes:
|
|
829
|
+
update_data["assignment"]["published"] = attributes["published"]
|
|
830
|
+
|
|
831
|
+
try:
|
|
832
|
+
assignment.edit(**update_data)
|
|
833
|
+
assignment._fetched_at = datetime.now()
|
|
834
|
+
if hasattr(assignment.course, "assignment_cache"):
|
|
835
|
+
assignment.course.assignment_cache[assignment.id] = (
|
|
836
|
+
assignment,
|
|
837
|
+
{},
|
|
838
|
+
)
|
|
839
|
+
logger.info(f"Updated assignment: {assignment.name}")
|
|
840
|
+
if "modules" in attributes:
|
|
841
|
+
module_regexes = attributes["modules"]
|
|
842
|
+
added, removed = modules.update_item_modules(
|
|
843
|
+
assignment.course,
|
|
844
|
+
"Assignment",
|
|
845
|
+
assignment.id,
|
|
846
|
+
module_regexes,
|
|
847
|
+
)
|
|
848
|
+
if added:
|
|
849
|
+
print(
|
|
850
|
+
f" Added to modules: {', '.join(added)}",
|
|
851
|
+
file=sys.stderr,
|
|
852
|
+
)
|
|
853
|
+
if removed:
|
|
854
|
+
print(
|
|
855
|
+
f" Removed from modules: {', '.join(removed)}",
|
|
856
|
+
file=sys.stderr,
|
|
857
|
+
)
|
|
858
|
+
except Exception as e:
|
|
859
|
+
canvaslms.cli.err(
|
|
860
|
+
1, f"Error updating assignment '{assignment.name}': {e}"
|
|
861
|
+
)
|
|
707
862
|
else:
|
|
708
863
|
try:
|
|
709
864
|
assignment_list = process_assignment_option(canvas, args)
|
|
@@ -891,6 +1046,8 @@ def add_assignments_command(subp):
|
|
|
891
1046
|
"view",
|
|
892
1047
|
help="View assignment details",
|
|
893
1048
|
description="Views detailed information about assignments. "
|
|
1049
|
+
"When piped (not a TTY), outputs YAML front matter with a 'regex' field "
|
|
1050
|
+
"for stable identification, followed by the description as Markdown. "
|
|
894
1051
|
"Use --html to preserve HTML instead of converting to Markdown.",
|
|
895
1052
|
)
|
|
896
1053
|
view_parser.set_defaults(func=assignments_view_command)
|
|
@@ -938,10 +1095,18 @@ def add_assignments_command(subp):
|
|
|
938
1095
|
description="Edit assignment content. Without -f, opens each matching "
|
|
939
1096
|
"assignment in your editor for interactive editing with preview. "
|
|
940
1097
|
"With -f, reads from a Markdown file with YAML front matter and updates "
|
|
941
|
-
"directly (script-friendly).
|
|
942
|
-
"
|
|
943
|
-
"
|
|
944
|
-
"
|
|
1098
|
+
"directly (script-friendly). "
|
|
1099
|
+
"Assignment matching workflow: "
|
|
1100
|
+
"(1) If the YAML contains a 'regex' field, it matches assignments by "
|
|
1101
|
+
"name or Canvas numeric ID. The regex is tested against both. "
|
|
1102
|
+
"This provides stable identification: export an assignment with "
|
|
1103
|
+
"'assignments view -a \"Lab 1.*\" > lab.md', edit locally, and push "
|
|
1104
|
+
"back with 'assignments edit -f lab.md'---the regex stays constant "
|
|
1105
|
+
"even if the name changes slightly. "
|
|
1106
|
+
"(2) If no 'regex' is present but 'name' is, exact name matching is "
|
|
1107
|
+
"used. "
|
|
1108
|
+
"(3) Otherwise, falls back to -a, -A, and -M filters. "
|
|
1109
|
+
"Use --html to read/edit HTML directly without Markdown conversion.",
|
|
945
1110
|
)
|
|
946
1111
|
edit_parser.set_defaults(func=assignments_edit_command)
|
|
947
1112
|
add_assignment_option(edit_parser, ungraded=False, required=True)
|
|
@@ -949,7 +1114,7 @@ def add_assignments_command(subp):
|
|
|
949
1114
|
edit_parser.add_argument(
|
|
950
1115
|
"--create",
|
|
951
1116
|
action="store_true",
|
|
952
|
-
help="Create a new assignment if
|
|
1117
|
+
help="Create a new assignment if no match is found",
|
|
953
1118
|
)
|
|
954
1119
|
edit_parser.add_argument(
|
|
955
1120
|
"--html",
|
canvaslms/cli/cli.nw
CHANGED
|
@@ -76,9 +76,19 @@ import argparse
|
|
|
76
76
|
\section{Printing errors and warnings}
|
|
77
77
|
|
|
78
78
|
We want uniform error handling.
|
|
79
|
+
Both [[EmptyListError]] and [[MultipleMatchesError]] represent query-result
|
|
80
|
+
conditions: the caller asked for items and got the wrong number.
|
|
81
|
+
Different callers handle these differently---interactive commands may catch
|
|
82
|
+
them locally and continue, while batch commands let them propagate to the
|
|
83
|
+
main handler for uniform treatment.
|
|
79
84
|
<<classes and exceptions>>=
|
|
80
85
|
class EmptyListError(Exception):
|
|
81
|
-
"""Exception raised when a
|
|
86
|
+
"""Exception raised when a query returns no matching items"""
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
class MultipleMatchesError(Exception):
|
|
90
|
+
"""Exception raised when a query matches multiple items
|
|
91
|
+
but exactly one was expected"""
|
|
82
92
|
pass
|
|
83
93
|
@
|
|
84
94
|
|
|
@@ -411,6 +421,11 @@ if args.func:
|
|
|
411
421
|
err(1, str(e))
|
|
412
422
|
else:
|
|
413
423
|
sys.exit(1)
|
|
424
|
+
except MultipleMatchesError as e:
|
|
425
|
+
if args.quiet == 0:
|
|
426
|
+
err(1, str(e))
|
|
427
|
+
else:
|
|
428
|
+
sys.exit(1)
|
|
414
429
|
@
|
|
415
430
|
|
|
416
431
|
We know that the [[login]] command doesn't need the Canvas object.
|