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

Fix parsing for calendar names containing '#' #761

Merged
merged 1 commit into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 36 additions & 14 deletions gcalcli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
# Everything you need to know (Google API Calendar v3): http://goo.gl/HfTGQ #
# #
# ######################################################################### #
from argparse import ArgumentTypeError
import json
import os
import pathlib
import re
import signal
import sys
from collections import namedtuple
Expand All @@ -45,23 +47,43 @@
"""


def parse_cal_names(cal_names):
def rsplit_unescaped_hash(string):
# Use regex to find parts before/after last unescaped hash separator.
# Sadly, all the "proper solutions" are even more questionable:
# https://stackoverflow.com/questions/4020539/process-escape-sequences
match = re.match(
r"""(?x)
^((?:\\.|[^\\])*)
[#]
((?:\\.|[^#\\])*)$
""",
string
)
if not match:
return (string, None)
# Unescape and return (part1, part2)
return tuple(re.sub(r'\\(.)', r'\1', p)
for p in match.group(1, 2))


def parse_cal_names(cal_names: list[str], printer: Printer):
cal_colors = {}
for name in cal_names:
cal_color = 'default'
parts = name.split('#')
parts_count = len(parts)
if parts_count >= 1:
cal_name = parts[0]

if len(parts) == 2:
cal_color = valid_color_name(parts[1])

if len(parts) > 2:
raise ValueError('Cannot parse calendar name: "%s"' % name)
p1, p2 = rsplit_unescaped_hash(name)
if p2 is not None:
try:
name, cal_color = p1, valid_color_name(p2)
except ArgumentTypeError:
printer.debug_msg(
f'Using entire name {name!r} as cal name.\n'
f'Change {p1!r} to a valid color name if intended to be a '
'color (or otherwise consider escaping "#" chars to "\\#").'
'\n'
)

cal_colors[cal_name] = cal_color
return [CalName(name=k, color=cal_colors[k]) for k in cal_colors.keys()]
cal_colors[name] = cal_color
return [CalName(name=k, color=v) for k, v in cal_colors.items()]


def run_add_prompt(parsed_args, printer):
Expand Down Expand Up @@ -376,7 +398,7 @@ def set_resolved_calendars(parsed_args, printer: Printer) -> list[str]:
'calendars may be coming from gcalclirc.\n'
)

cal_names = parse_cal_names(parsed_args.calendars)
cal_names = parse_cal_names(parsed_args.calendars, printer=printer)
# Only ignore calendars if they're not explicitly in --calendar list.
parsed_args.ignore_calendars[:] = [
c
Expand Down
22 changes: 12 additions & 10 deletions tests/test_gcalcli.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def test_cal_query(capsys, PatchedGCalI):


def test_add_event(PatchedGCalI):
cal_names = parse_cal_names(['[email protected]'])
cal_names = parse_cal_names(['[email protected]'], printer=None)
gcal = PatchedGCalI(
cal_names=cal_names, allday=False, default_reminders=True)
assert gcal.AddEvent(title='test event',
Expand All @@ -109,7 +109,8 @@ def test_add_event(PatchedGCalI):


def test_add_event_with_cal_prompt(PatchedGCalI, capsys, monkeypatch):
cal_names = parse_cal_names(['[email protected]', '[email protected]'])
cal_names = parse_cal_names(
['[email protected]', '[email protected]'], None)
gcal = PatchedGCalI(
cal_names=cal_names, allday=False, default_reminders=True)
# Fake selecting calendar 0 at the prompt
Expand All @@ -131,7 +132,7 @@ def test_add_event_with_cal_prompt(PatchedGCalI, capsys, monkeypatch):
def test_add_event_override_color(capsys, default_options,
PatchedGCalIForEvents):
default_options.update({'override_color': True})
cal_names = parse_cal_names(['[email protected]'])
cal_names = parse_cal_names(['[email protected]'], None)
gcal = PatchedGCalIForEvents(cal_names=cal_names, **default_options)
gcal.AgendaQuery()
captured = capsys.readouterr()
Expand All @@ -141,15 +142,16 @@ def test_add_event_override_color(capsys, default_options,


def test_quick_add(PatchedGCalI):
cal_names = parse_cal_names(['[email protected]'])
cal_names = parse_cal_names(['[email protected]'], None)
gcal = PatchedGCalI(cal_names=cal_names)
assert gcal.QuickAddEvent(
event_text='quick test event',
reminders=['5m sms'])


def test_quick_add_with_cal_prompt(PatchedGCalI, capsys, monkeypatch):
cal_names = parse_cal_names(['[email protected]', '[email protected]'])
cal_names = parse_cal_names(
['[email protected]', '[email protected]'], None)
gcal = PatchedGCalI(cal_names=cal_names)
# Fake selecting calendar 0 at the prompt
monkeypatch.setattr('sys.stdin', io.StringIO('0\n'))
Expand Down Expand Up @@ -271,14 +273,14 @@ def test_modify_event(PatchedGCalI):


def test_import(PatchedGCalI):
cal_names = parse_cal_names(['[email protected]'])
cal_names = parse_cal_names(['[email protected]'], None)
gcal = PatchedGCalI(cal_names=cal_names, default_reminders=True)
vcal_path = TEST_DATA_DIR + '/vv.txt'
assert gcal.ImportICS(icsFile=open(vcal_path, errors='replace'))


def test_legacy_import(PatchedGCalI):
cal_names = parse_cal_names(['[email protected]'])
cal_names = parse_cal_names(['[email protected]'], None)
gcal = PatchedGCalI(
cal_names=cal_names, default_reminders=True, use_legacy_import=True)
vcal_path = TEST_DATA_DIR + '/vv.txt'
Expand Down Expand Up @@ -323,15 +325,15 @@ def test_parse_cal_names(PatchedGCalI):
# and then assert the right number of events
# for the moment, we assert 0 (which indicates successful completion of
# the code path, but no events printed)
cal_names = parse_cal_names(['j*#green'])
cal_names = parse_cal_names(['j*#green'], None)
gcal = PatchedGCalI(cal_names=cal_names)
assert gcal.AgendaQuery() == 0

cal_names = parse_cal_names(['j*'])
cal_names = parse_cal_names(['j*'], None)
gcal = PatchedGCalI(cal_names=cal_names)
assert gcal.AgendaQuery() == 0

cal_names = parse_cal_names(['[email protected]'])
cal_names = parse_cal_names(['[email protected]'], None)
gcal = PatchedGCalI(cal_names=cal_names)
assert gcal.AgendaQuery() == 0

Expand Down
Loading