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

Key Account.transactions by account type instead of str #49

Merged

Conversation

teeberg
Copy link
Contributor

@teeberg teeberg commented Mar 4, 2023

In account.py, the transactions field is defined as:

transactions: Dict[str, TransactionList] = {}

But there were a few places where we were using an instance of the AccountType enum as the key instead of the string, which could result in invalid QIF files being produced that read things like

!Type:AccountType.CASH

instead of

!Type:Cash

@isaacharrisholt
Copy link
Owner

I'm not sure what my preference would be here. I think I'd actually lean towards making the transactions field a Dict[AccountType, TransactionList] and altering the output methods to use the enum values, rather than the other way around.

What are your thoughts on this? The biggest downside is that it wouldn't be retro-compatible.

@teeberg
Copy link
Contributor Author

teeberg commented Mar 4, 2023

I was thinking the exact same. I would also prefer making the transactions a Dict[AccountType, TransactionList], but since that's not backwards-compatible, I decided to pass strings instead. :)

I think changing it to AccountType could fairly easily be rolled out by releasing a new minor version that allows both and logs a warning if you pass in a str, to be backwards-compatible, and then either immediately or in a month or so release a new major version that only allows AccountType to be passed in.

@isaacharrisholt
Copy link
Owner

I agree, though I would use a new hotfix version for the initial one, and a minor version later. Would you be willing to make the initial changes in this PR?

@teeberg
Copy link
Contributor Author

teeberg commented Mar 4, 2023

@isaacharrisholt Sure, did that. What do you think of the latest commit?

Comment on lines 115 to 118
warnings.warn(f'You passed in a value of {header!r} for argument "header", '
f'but it should have been of type "AccountType". Starting with '
f'the next major release, this will become a type error.',
DeprecationWarning)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of things: next *minor release

Secondly, could we format with a hanging indent? :)

            warnings.warn(
                f'You passed in a value of {header!r} for argument "header", '
                f'but it should have been of type "AccountType". Starting with '
                f'the next major release, this will become a type error.',
                DeprecationWarning,
           )

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the second and third lines don't need to be f-strings

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you looking to follow semver? According to semver, a breaking change requires a major release, which is why I wrote this here. :) Happy to change the copy here, though, if you don't mind.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and, changed it to a hanging indent

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh really? I thought a breaking change could have a minor release, and it was just hotfixes that had to be version compatible? Maybe I need to do some reading...

@isaacharrisholt
Copy link
Owner

Actually, thinking about this, I believe Pydantic will automatically convert the string to an enum if it's valid, will it not?

Comment on lines 134 to 151
header = AccountType(header.strip().split(':')[-1])
self._last_header = header
self._last_header = self._convert_deprecated_str_header(header)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading this again, I'm perhaps a little confused. If we were always converting to an account type, could we not just change the type hint for the transactions field?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were not necessarily always converting since you can also call .set_header() manually outside of this function

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, but surely we could just do self.header = AccountType(header) there? That should work fine, and throw and error if it's an invalid account type, which we want? I don't mind people being able to pass in strings (hence the strings everywhere in the docs and tests), as long as things end up the right types in the objects. Also, everything is pretty well type-hinted, so people's IDEs should scream at them if they try and use the wrong things in the wrong places.

At the end of the day, there's only so much defensive programming we can do. Python is inherently flexible, and you'll always be able to break things like this.

Copy link
Contributor Author

@teeberg teeberg Mar 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy to change that, but that could potentially be a breaking change, if people used .set_header() to set other strings that are not valid QIF account types. Shall I?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not a breaking change if it fixes unintended behaviour 🤷🏻‍♂️

I think we should probably fix this, then run a static type checker like pyright against the codebase. I'm also tempted to introduce black for formatting. That's what I've done with iTmpl and it makes life so much easier.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, fine with me!

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's just make sure to squash the commits on this PR before merging 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, will do :D

@teeberg
Copy link
Contributor Author

teeberg commented Mar 4, 2023

Actually, thinking about this, I believe Pydantic will automatically convert the string to an enum if it's valid, will it not?

I briefly tested this and it doesn't seem to, as far as I can tell

@teeberg teeberg force-pushed the fix-invalid-transaction-header branch from 0889008 to 3b10266 Compare March 4, 2023 12:57
@teeberg teeberg changed the title Fix invalid transaction header Key Account.transactions by account type instead of str Mar 4, 2023
@teeberg teeberg changed the title Key Account.transactions by account type instead of str Key Account.transactions by account type instead of str Mar 4, 2023
@teeberg
Copy link
Contributor Author

teeberg commented Mar 4, 2023

Squashed everything. How does that look?

@teeberg teeberg force-pushed the fix-invalid-transaction-header branch from 3b10266 to 705fb12 Compare March 4, 2023 13:01
@teeberg
Copy link
Contributor Author

teeberg commented Mar 4, 2023

Actually, thinking about this, I believe Pydantic will automatically convert the string to an enum if it's valid, will it not?

Tested this some more. You're right it in that it automatically converts str to enum during object instantiation in a case like this:

image

It does not to do that at any later time, though, e.g. when re-setting any of the attributes.

@isaacharrisholt
Copy link
Owner

Tested this some more. You're right it in that it automatically converts str to enum during object instantiation in a case like this:

Yeah, I thought so. That's one of the benefits of Pydantic.

@@ -125,6 +125,7 @@ def test_add_transaction_invalid_header():
account = Account(name='Test Account')
transaction = Transaction(date=datetime(2022, 2, 1), amount=0)
with pytest.raises(ValueError):
# noinspection PyTypeChecker
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are these comments for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shuts up type warnings in the IDE:

image

image

Do you want me to take those out?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took them out :)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I think they're perhaps specific to your editor

Copy link
Contributor Author

@teeberg teeberg Mar 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think they're IntelliJ-specific

@isaacharrisholt
Copy link
Owner

@teeberg if you're interested, I've started work on the tooling improvements in #50

I'll wait for this one to be done first though, as it'll affect typing stuff.

@teeberg teeberg force-pushed the fix-invalid-transaction-header branch from 51d6ef6 to 8ef6259 Compare March 4, 2023 13:46
@isaacharrisholt
Copy link
Owner

Do you have merge permissions?

@isaacharrisholt isaacharrisholt merged commit bd15a6d into isaacharrisholt:main Mar 4, 2023
@teeberg
Copy link
Contributor Author

teeberg commented Mar 4, 2023

Do you have merge permissions?

I don't think so! Thanks for merging! :)

@teeberg teeberg deleted the fix-invalid-transaction-header branch March 4, 2023 14:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants