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

Write and test BlockNode, ListNode #73

Merged
merged 17 commits into from
Nov 24, 2019

Conversation

sydneyli
Copy link
Contributor

@sydneyli sydneyli commented Oct 4, 2019

From interface described in #49, depends on #72. Should be rebased/merged when #72 lands.

@codecov-io
Copy link

codecov-io commented Oct 4, 2019

Codecov Report

Merging #73 into master will increase coverage by 3.19%.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #73      +/-   ##
==========================================
+ Coverage   88.34%   91.54%   +3.19%     
==========================================
  Files           7        6       -1     
  Lines         678      627      -51     
==========================================
- Hits          599      574      -25     
+ Misses         79       53      -26
Impacted Files Coverage Δ
apacheconfig/__init__.py 100% <ø> (ø) ⬆️
apacheconfig/parser.py 85.45% <0%> (-0.52%) ⬇️
apacheconfig/lexer.py 96.65% <0%> (+0.03%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 5741248...b7baae8. Read the comment docs.

@sydneyli sydneyli force-pushed the writable-loader-contents branch from db18f4a to 9aed274 Compare October 8, 2019 00:40
Copy link
Owner

@etingof etingof left a comment

Choose a reason for hiding this comment

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

Thank you! Looks good, my main concerns are:

  • Do we really need ContentsNode abstraction?
  • I guess I am not yet understanding specialized parser suite creation frequency - it would be great to have it reusable as much as possible

WDYT?

"""


class ContentsNode(Node):
Copy link
Owner

Choose a reason for hiding this comment

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

How much ContentsNode is different from BlockNode? I can imagine, structurally, having BlockNode everywhere (root, intermediate nodes) and ItemNode & BlockNode in it.

My understanding is that with ContentsNode we will have them scoped like:

ContentsNode (root) -> BlockNode -> ContentsNode -> BlockNode -> ItemNode

So I am wondering if we can always treat BlockNode as a "container", where ItemNode would always be a leaf scalar e.g.:

BlockNode (root) -> BlockNode -> ItemNode

WDYT?

@etingof
Copy link
Owner

etingof commented Nov 5, 2019

@sydneyli I assume this PR is not blocked on me, presently. ;-)

@sydneyli
Copy link
Contributor Author

sydneyli commented Nov 6, 2019

@sydneyli I assume this PR is not blocked on me, presently. ;-)

Yep, it's back to me! Hoping to get to this today.

@sydneyli
Copy link
Contributor Author

sydneyli commented Nov 7, 2019

Re: ContentsNode vs BlockNode:

It's a good point. I do see what you mean that ContentsNode vs BlockNode are really only unique in that contents has no tag, and effectively acts as a root node (or as a child of BlockNode), and BlockNode is intermediary.

Here are the things that make the two different:

  • BlockNode isolates the logic for manipulating & accessing the tag name & values.
  • BlockNode keeps track of the whitespace preceding the first tag. ContentsNode keeps track of the whitespace after the final scalar in the group (like, the whitespace before EOF, or before the closing tag of a block)

Having these (particularly the tag manipulation) separated by class type seems useful. What if we have BlockNode subclass a generic ContentsNode, and maybe rename them something like MidNode and RootNode, respectively? Something that'd come up is that the whitespace property of the classes would conflict, so ContentsNode would have to keep a unique trailing_whitespace property.

@etingof
Copy link
Owner

etingof commented Nov 7, 2019

Having these (particularly the tag manipulation) separated by class type seems useful. What if we have BlockNode subclass a generic ContentsNode, and maybe rename them something like MidNode and RootNode, respectively? Something that'd come up is that the whitespace property of the classes would conflict, so ContentsNode would have to keep a unique trailing_whitespace property.

That sounds like a legit design to me. Alternatively to subclassing, may be we could have an abstract or a base class declaring the interfaces and/or common implementation, then RootNode and BlockNode would have their own unique stuff.

In other words:

AbstractNode -> CommonNodeMixIn + RootNode
AbstractNode -> CommonNodeMixIn + BlockNode

or

NodeBase -> RootNode
NodeBase -> BlockNode

The mix-in piece would be there to keep abstract class defining just the interfaces, not any common code. On the other hand, that's not strictly required with Python customs, AFAIK.

WDYT?

@sydneyli
Copy link
Contributor Author

sydneyli commented Nov 7, 2019

Alternatively to subclassing, may be we could have an abstract or a base class declaring the interfaces and/or common implementation, then RootNode and BlockNode would have their own unique stuff.

Subclassing might make things a bit simpler here, since BlockNode has a lot of unique functionality over RootNode, but not the other way around. That is, the set of things RootNode should be able to do is actually a subset of the things BlockNode can do.

EDIT: This was a pretty straightforward change, so I tried it out in the most recent commit. Let me know what you think, or if you might prefer having an abstract class. Also, naming is still hard, so will take recommendations there :)

Also remove exported *Node objects from __init__ since we don't
necessarily want to expose those constructors.
Copy link
Owner

@etingof etingof left a comment

Choose a reason for hiding this comment

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

Looks awesome to me!

I have a handful of nits for your considerations.


@property
def arguments(self):
"""Returns arguments for this block as a literal string.
Copy link
Owner

Choose a reason for hiding this comment

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

literal string - is it in Apache config syntax?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope-- removed to just say "string".

@etingof etingof changed the title Write and test BlockNode, ContentsNode Write and test BlockNode, ListNode Nov 9, 2019
@sydneyli
Copy link
Contributor Author

sydneyli commented Nov 13, 2019

Thanks for the review! I did a pass on the documentation. Currently working on error handling documentation & tests, as well testing for unicode support.

EDIT: Did a pass on error handling. Unicode support might be a larger undertaking, so I opened #85 !

@sydneyli
Copy link
Contributor Author

(Just a note in case you missed-- this and the unicode PR are good for another round when you get the chance!)

@etingof
Copy link
Owner

etingof commented Nov 24, 2019

(Just a note in case you missed-- this and the unicode PR are good for another round when you get the chance!)

Hey, and thank you for the ping!

I am not always sure at which point it's back to me (I am checking out the PRs from occasionally), so explicit communication is good!

Every AST should have a :class:`apacheconfig.ListNode` at its root. A
:class:`apacheconfig.ListNode` or :class:`apacheconfig.BlockNode` can have
other :class:`apacheconfig.BlockNode`s and
:class:`apacheconfig.LeafASTNode`s as children.
Copy link
Owner

Choose a reason for hiding this comment

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

nit: Is it deliberate to have AST in abstract and leaf, but not in other class names?

root should be a ListNode. To construct from a raw string, use the
`parse` factory function. The default __init__ constructor expects data
from the internal apacheconfig parser.
Every configuration file's root should be a ListNode.
Copy link
Owner

Choose a reason for hiding this comment

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

nit: hyperlink ListNode?

@etingof
Copy link
Owner

etingof commented Nov 24, 2019

Let's merge this PR - I think it's good enough and I do not want to block you.

I've left a couple of nits for your consideration (I am never short of nits). If you consider them worth addressing, I guess you could just push a quick PR.

Also, perhaps at some point we should have a CHANGELOG entry explaining this undertaking. I am just not sure at which PR in the chain it's would be best to note.

Thank you!

@etingof etingof merged commit 896349e into etingof:master Nov 24, 2019
@sydneyli sydneyli deleted the writable-loader-contents branch November 25, 2019 16:07
@sydneyli
Copy link
Contributor Author

Thanks @etingof -- appreciate it! I agree with your notes on consistency between the namings, so i'll push a PR for that soon.

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.

3 participants