Skip to content

Commit

Permalink
Fixed parser handling
Browse files Browse the repository at this point in the history
- made f-string conversions
- minor linting fixes
- Added tests
  • Loading branch information
rsb-23 committed Jan 21, 2025
1 parent 27bec6b commit 1f56ee8
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 88 deletions.
28 changes: 28 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import subprocess


class Cli:
ics_diff = "ics_diff"
change_tz = "change_tz"


def run_cli_tool(toolname: str, args: list[str]):
return subprocess.run([toolname] + args, capture_output=True, text=True, check=False)


def test_change_tz():
result = run_cli_tool(Cli.change_tz, ["--version"])
assert result.returncode == 0

result = run_cli_tool(Cli.change_tz, [])
assert result.returncode == 2
assert "one of the arguments -l/--list ics_file is required" in result.stderr


def test_ics_diff():
result = run_cli_tool(Cli.ics_diff, ["--version"])
assert result.returncode == 0

result = run_cli_tool(Cli.ics_diff, [])
assert result.returncode == 2
assert "required: ics_file1, ics_file2" in result.stderr
47 changes: 19 additions & 28 deletions vobject/change_tz.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import vobject

version = "0.1"
version = vobject.VERSION


def change_tz(cal, new_timezone, default, utc_only=False, utc_tz=vobject.icalendar.utc):
Expand Down Expand Up @@ -40,53 +40,44 @@ def show_timezones():
print(tz_string)


def convert_events(utc_only, args):
print("Converting {} events".format("only UTC" if utc_only else "all"))
ics_file = args[0]
_tzone = args[1] if len(args) > 1 else "UTC"
def convert_events(utc_only, ics_file, timezone_="UTC"):
print(f'Converting {"only UTC" if utc_only else "all"} events')

print("... Reading {}".format(ics_file))
print(f"... Reading {ics_file}")
with open(ics_file, "r") as f:
cal = vobject.readOne(f)
change_tz(cal, new_timezone=tz.gettz(_tzone), default=tz.gettz("UTC"), utc_only=utc_only)
change_tz(cal, new_timezone=tz.gettz(timezone_), default=tz.gettz("UTC"), utc_only=utc_only)

out_name = "{}.converted".format(ics_file)
print("... Writing {}".format(out_name))
out_name = f"{ics_file}.converted"
print(f"... Writing {out_name}")
with open(out_name, "wb") as out:
cal.serialize(out)

print("Done")


def main():
options, args = get_options()

if options.list:
args = get_arguments()
if args.list:
show_timezones()
elif args:
convert_events(utc_only=options.utc, args=args)
elif args.ics_file:
convert_events(utc_only=args.utc, ics_file=args.ics_file, timezone_=args.timezone)


def get_options():
def get_arguments():
# Configuration options
usage = """usage: %prog [options] ics_file [timezone]"""
parser = ArgumentParser(usage=usage, description="change_tz will convert the timezones in an ics file.")
parser.add_argument("--version", action="version", version=vobject.VERSION)
parser = ArgumentParser(description="change_tz will convert the timezones in an ics file.")
parser.add_argument("-V", "--version", action="version", version=vobject.VERSION)

parser.add_argument(
"-u", "--only-utc", dest="utc", action="store_true", default=False, help="Only change UTC events."
)
parser.add_argument(
"-l", "--list", dest="list", action="store_true", default=False, help="List available timezones"
)

(cmdline_options, args) = parser.parse_args()
if not (args or cmdline_options.list):
print("error: too few arguments given")
print(parser.format_help())
return cmdline_options, False
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("-l", "--list", dest="list", action="store_true", default=False, help="List available timezones")
group.add_argument("ics_file", nargs="?", help="The ics file to process")
parser.add_argument("timezone", nargs="?", default="UTC", help="The timezone to convert to")

return cmdline_options, args
return parser.parse_args()


if __name__ == "__main__":
Expand Down
109 changes: 49 additions & 60 deletions vobject/ics_diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ def getSortKey(component):
def getUID(component):
return component.getChildValue("uid", "")

# it's not quite as simple as getUID, need to account for recurrenceID and
# sequence
# it's not quite as simple as getUID, need to account for recurrenceID and sequence

def getSequence(component):
sequence = component.getChildValue("sequence", 0)
Expand Down Expand Up @@ -94,11 +93,10 @@ def processComponentLists(leftList, rightList):
def newComponent(name, body): # pylint:disable=unused-variable
if body is None:
return None
else:
c = vobject.base.Component(name)
c.behavior = vobject.base.getBehavior(name)
c.isNative = True
return c
c = vobject.base.Component(name)
c.behavior = vobject.base.getBehavior(name)
c.isNative = True
return c

def processComponentPair(leftComp, rightComp):
"""
Expand Down Expand Up @@ -129,36 +127,36 @@ def processComponentPair(leftComp, rightComp):
else:
differentContentLines.append(([], rightComp.contents[key]))

if len(differentContentLines) == 0 and len(differentComponents) == 0:
if not differentContentLines and not differentComponents:
return None
else:
left = vobject.newFromBehavior(leftComp.name)
right = vobject.newFromBehavior(leftComp.name)
# add a UID, if one existed, despite the fact that they'll always be
# the same
uid = leftComp.getChildValue("uid")
if uid is not None:
left.add("uid").value = uid
right.add("uid").value = uid

for name, childPairList in differentComponents.items():
leftComponents, rightComponents = zip(*childPairList)
if len(leftComponents) > 0:
# filter out None
left.contents[name] = filter(None, leftComponents)
if len(rightComponents) > 0:
# filter out None
right.contents[name] = filter(None, rightComponents)

for leftChildLine, rightChildLine in differentContentLines:
nonEmpty = leftChildLine or rightChildLine
name = nonEmpty[0].name
if leftChildLine is not None:
left.contents[name] = leftChildLine
if rightChildLine is not None:
right.contents[name] = rightChildLine

return left, right

left = vobject.newFromBehavior(leftComp.name)
right = vobject.newFromBehavior(leftComp.name)
# add a UID, if one existed, despite the fact that they'll always be
# the same
uid = leftComp.getChildValue("uid")
if uid is not None:
left.add("uid").value = uid
right.add("uid").value = uid

for name, childPairList in differentComponents.items():
leftComponents, rightComponents = zip(*childPairList)
if len(leftComponents) > 0:
# filter out None
left.contents[name] = filter(None, leftComponents)
if len(rightComponents) > 0:
# filter out None
right.contents[name] = filter(None, rightComponents)

for leftChildLine, rightChildLine in differentContentLines:
nonEmpty = leftChildLine or rightChildLine
name = nonEmpty[0].name
if leftChildLine is not None:
left.contents[name] = leftChildLine
if rightChildLine is not None:
right.contents[name] = rightChildLine

return left, right

vevents = processComponentLists(
sortByUID(getattr(left, "vevent_list", [])), sortByUID(getattr(right, "vevent_list", []))
Expand All @@ -183,24 +181,19 @@ def prettyDiff(leftObj, rightObj):


def main():
options, args = getOptions()
if args:
ignore_dtstamp = options.ignore
ics_file1, ics_file2 = args
with open(ics_file1) as f, open(ics_file2) as g:
cal1 = vobject.readOne(f)
cal2 = vobject.readOne(g)
deleteExtraneous(cal1, ignore_dtstamp=ignore_dtstamp)
deleteExtraneous(cal2, ignore_dtstamp=ignore_dtstamp)
prettyDiff(cal1, cal2)


def getOptions():
# Configuration options #
args = get_arguments()
with open(args.ics_file1) as f, open(args.ics_file2) as g:
cal1 = vobject.readOne(f)
cal2 = vobject.readOne(g)
deleteExtraneous(cal1, ignore_dtstamp=args.ignore)
deleteExtraneous(cal2, ignore_dtstamp=args.ignore)
prettyDiff(cal1, cal2)

usage = "usage: %prog [options] ics_file1 ics_file2"
parser = ArgumentParser(usage=usage, description="ics_diff will print a comparison of two iCalendar files")
parser.add_argument("--version", action="version", version=vobject.VERSION)

def get_arguments():
# Configuration options #
parser = ArgumentParser(description="ics_diff will print a comparison of two iCalendar files")
parser.add_argument("-V", "--version", action="version", version=vobject.VERSION)
parser.add_argument(
"-i",
"--ignore-dtstamp",
Expand All @@ -209,14 +202,10 @@ def getOptions():
default=False,
help="ignore DTSTAMP lines [default: False]",
)
parser.add_argument("ics_file1", help="The first ics file to compare")
parser.add_argument("ics_file2", help="The second ics file to compare")

(cmdline_options, args) = parser.parse_args()
if len(args) < 2:
print("error: too few arguments given\n")
print(parser.format_help())
return False, False

return cmdline_options, args
return parser.parse_args()


if __name__ == "__main__":
Expand Down

0 comments on commit 1f56ee8

Please sign in to comment.