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

Add ParserConfig class to encapsulate file path settings #439

Merged
merged 24 commits into from
Feb 4, 2021

Conversation

azeey
Copy link
Collaborator

@azeey azeey commented Dec 15, 2020

Toward #373

This adds a new ParserConfig object that contains configuration options for the libsdformat parser. This replaces the old globals g_uriPathMap and g_findFileCB. To do this in a backward compatible way, the ParserConfig class provides a singleton object that calls to sdf::setSdfFindCallback and sdf::addURIPath use.

New overloads of the main SDFormat file and string loading APIs allow users to pass in a ParserConfig object. If omitted, these APIs will default to the singleton ParserConfig object.

The error message assumes model uri's always start with "model://". This
is no longer the case.

Signed-off-by: Addisu Z. Taddese <addisu@openrobotics.org>
Signed-off-by: Addisu Z. Taddese <addisu@openrobotics.org>
@azeey azeey added the 🏢 edifice Ignition Edifice label Dec 15, 2020
@azeey azeey added this to the SDFormat 1.8 / libsdformat11 milestone Dec 15, 2020
@azeey azeey self-assigned this Dec 15, 2020
@azeey azeey requested a review from brawner December 15, 2020 16:53
Signed-off-by: Addisu Z. Taddese <addisu@openrobotics.org>
Signed-off-by: Addisu Z. Taddese <addisu@openrobotics.org>
@codecov-io
Copy link

codecov-io commented Dec 15, 2020

Codecov Report

Merging #439 (b9bb22b) into master (fc4dc8c) will increase coverage by 0.14%.
The diff coverage is 99.01%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #439      +/-   ##
==========================================
+ Coverage   87.60%   87.75%   +0.14%     
==========================================
  Files          63       64       +1     
  Lines        9616     9668      +52     
==========================================
+ Hits         8424     8484      +60     
+ Misses       1192     1184       -8     
Impacted Files Coverage Δ
src/parser.cc 80.35% <97.87%> (+1.21%) ⬆️
src/ParserConfig.cc 100.00% <100.00%> (ø)
src/Root.cc 96.59% <100.00%> (+0.09%) ⬆️
src/SDF.cc 93.33% <100.00%> (+0.39%) ⬆️

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 fc4dc8c...b9bb22b. Read the comment docs.

Copy link
Collaborator

@brawner brawner left a comment

Choose a reason for hiding this comment

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

I don't feel confident enough in the code base to approve/request changes yet, so I'll leave that to other reviewers. But I did have a few comments.

My main question is whether the DefaultConfig() should be a mutable singleton. Typically a default cannot be changed by client code, so I would expect the API to return a static const ParserConfig &. What were the tradeoffs compared to passing around a reference to a config object in the parsing code, with sdf::Root::Load() and related functions taking a default argument?

If the mutable singleton is the best approach here, what would you think about changing it's name to GlobalConfig()?

src/ParserConfig.cc Outdated Show resolved Hide resolved
include/sdf/ParserConfig.hh Outdated Show resolved Hide resolved
Signed-off-by: Addisu Z. Taddese <addisu@openrobotics.org>
Signed-off-by: Addisu Z. Taddese <addisu@openrobotics.org>
Copy link
Collaborator Author

@azeey azeey left a comment

Choose a reason for hiding this comment

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

If the mutable singleton is the best approach here, what would you think about changing it's name to GlobalConfig()?

I thought a mutable singleton was needed to maintain backward compatibility. The functions sdf::setFindFileCallback and sdf::addURIPath used to modify the globals g_findFileCB and g_uriPathMap respectively. Now they modify the singleton. But I do agree that GlobalConfig is a much better name. Renamed in deda523

src/ParserConfig.cc Outdated Show resolved Hide resolved
include/sdf/ParserConfig.hh Outdated Show resolved Hide resolved
Signed-off-by: Addisu Z. Taddese <addisu@openrobotics.org>
@azeey
Copy link
Collaborator Author

azeey commented Dec 19, 2020

@osrf-jenkins run tests please

@azeey
Copy link
Collaborator Author

azeey commented Dec 19, 2020

Not sure why the Jenkins CI status is not updating even though they are finished:

  • Ubuntu Build Status
  • Homebrew Build Status
  • Windows Build Status

@EricCousineau-TRI
Copy link
Collaborator

May need rebase or merge (again)?

image

@@ -40,22 +40,60 @@ namespace sdf
//
class Root;

/// \brief Init based on the installed sdf_format.xml file
Copy link
Collaborator

Choose a reason for hiding this comment

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

BTW This is a large amount of combinatorics... But I don't have a better solution.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah :(. I think there are some redundancies that we can deprecate, but we still end up with a lot of functions.

Copy link
Collaborator

@EricCousineau-TRI EricCousineau-TRI left a comment

Choose a reason for hiding this comment

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

First pass done, PTAL!

include/sdf/parser.hh Outdated Show resolved Hide resolved
// Forward declare private data class.
class ParserConfigPrivate;

/// This class contains configuration options for the libsdformat parser.
Copy link
Collaborator

Choose a reason for hiding this comment

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

In addition to below comment (how path resolution works), it's unclear to me what the precedence is for these different attributes (e.g. does callback override search paths, or the other way around).

Is there documentation that this can forward-reference, or should it be written here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I added some documentation in SDFImpl.hh (6f53a9f) and referenced it here in 1d321e0

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yay! Thanks!

@@ -64,6 +65,21 @@ namespace sdf
bool _searchLocalPath = true,
bool _useCallback = false);

/// \brief Find the absolute path of a file.
Copy link
Collaborator

Choose a reason for hiding this comment

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

This new function, and the old flavor, don't fully specify how the searching actually takes place. (I understand it's implementation specific, but still.)

From _useCallback, I infer that "normal mechanism" means _searchLocalPath, but I don't know it for sure.
Can you make this text more concise, and tell what the priorities are?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't know about more concise, but I listed the search order in 6f53a9f. How does that look?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ooh, that's excellent! Thank you!


/// \brief Get the URI scheme to search directories map
/// \return Immutable reference to the URI scheme to search directories map
public: const SchemeToPathMap &URIPathMap() const;
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit It smells weird to be able to look at all paths, add a new path, but not remove a path. (assymmetry)

Is it hard to add that here?

Or perhaps the right workflow is to just reassign? e.g. just clear it out.

So if I wanted to remove or reorder a path, I would just do something like Parser::GLobalConfig() = my_new_config?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was trying to make this PR replicate existing functionality without adding any new features other than being able to specify a non-global config. It would be easy to add a function to remove a URI scheme, but adding a path to an existing URI scheme or reordering paths seem best suited for a future PR, IMO.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Aye, makes sense to me.

@@ -0,0 +1,240 @@
/*
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are there any tests which explicitly show shadowing / prioritization?
e.g. both a search path and a callback could resolve the same file (possibly to different places), but only one of them wins out?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added test in 8977cd1

Copy link
Member

@scpeters scpeters left a comment

Choose a reason for hiding this comment

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

there is a SDFPtr readFile(const std::string &) at parser.hh:105 that didn't get overloaded

also, #474 was just merged, so you can use ImplPtr in ParserConfig

include/sdf/ParserConfig.hh Outdated Show resolved Hide resolved
include/sdf/ParserConfig.hh Outdated Show resolved Hide resolved
include/sdf/ParserConfig.hh Show resolved Hide resolved
include/sdf/parser.hh Outdated Show resolved Hide resolved
Signed-off-by: Addisu Z. Taddese <addisu@openrobotics.org>
Signed-off-by: Addisu Z. Taddese <addisu@openrobotics.org>
@azeey
Copy link
Collaborator Author

azeey commented Feb 2, 2021

there is a SDFPtr readFile(const std::string &) at parser.hh:105 that didn't get overloaded

I was trying to have one less overload by only overloading the signature that also contains an sdf::Error. I did the same for readString.

Here's a list of all the functions in parser.hh that work with ParserConfig. I think it helps to see them without the documentation:

bool init(SDFPtr _sdf);

bool initFile(const std::string &_filename, SDFPtr _sdf);
bool initFile(const std::string &_filename, const ParserConfig &_config, SDFPtr _sdf);

bool initFile(const std::string &_filename, ElementPtr _sdf);
bool initFile(const std::string &_filename, const ParserConfig &_config, ElementPtr _sdf);

bool initString(const std::string &_xmlString, SDFPtr _sdf);
bool initString(const std::string &_xmlString, const ParserConfig &_config, SDFPtr _sdf);

sdf::SDFPtr readFile(const std::string &_filename);
sdf::SDFPtr readFile(const std::string &_filename, Errors &_errors);
sdf::SDFPtr readFile(const std::string &_filename, const ParserConfig &_config, Errors &_errors);

bool readFile(const std::string &_filename, SDFPtr _sdf);
bool readFile(const std::string &_filename, SDFPtr _sdf, Errors &_errors);
bool readFile(const std::string &_filename, const ParserConfig &_config, SDFPtr _sdf, Errors &_errors);

bool readFileWithoutConversion(const std::string &_filename, SDFPtr _sdf, Errors &_errors);
bool readFileWithoutConversion(const std::string &_filename, const ParserConfig &_config, SDFPtr _sdf, Errors &_errors);

bool readString(const std::string &_xmlString, SDFPtr _sdf);
bool readString(const std::string &_xmlString, SDFPtr _sdf, Errors &_errors);
bool readString(const std::string &_xmlString, const ParserConfig &_config, SDFPtr _sdf, Errors &_errors);

bool readString(const std::string &_xmlString, ElementPtr _sdf);
bool readString(const std::string &_xmlString, ElementPtr _sdf, Errors &_errors);
bool readString(const std::string &_xmlString, const ParserConfig &_config, ElementPtr _sdf, Errors &_errors);

bool readStringWithoutConversion( const std::string &_xmlString, SDFPtr _sdf, Errors &_errors);
bool readStringWithoutConversion(const std::string &_xmlString, const ParserConfig &_config, SDFPtr _sdf, Errors &_errors);

bool convertFile(const std::string &_filename, const std::string &_version, SDFPtr _sdf);
bool convertFile(const std::string &_filename, const std::string &_version, const ParserConfig &_config, SDFPtr _sdf);

bool convertString(const std::string &_sdfString, const std::string &_version, SDFPtr _sdf);
bool convertString(const std::string &_sdfString, const std::string &_version, const ParserConfig &_config, SDFPtr _sdf);

It's interesting that we have sdf::SDFPtr readFile(const std::string &_filename); and bool readString(const std::string &_xmlString, SDFPtr _sdf);. I'm guessing the latter was added along with bool readString(const std::string &_xmlString, ElementPtr _sdf);. Should we deprecate sdf::SDFPtr readFile(const std::string &_filename);?

Signed-off-by: Addisu Z. Taddese <addisu@openrobotics.org>
Signed-off-by: Addisu Z. Taddese <addisu@openrobotics.org>
Signed-off-by: Addisu Z. Taddese <addisu@openrobotics.org>
Signed-off-by: Addisu Z. Taddese <addisu@openrobotics.org>
Signed-off-by: Addisu Z. Taddese <addisu@openrobotics.org>
@scpeters
Copy link
Member

scpeters commented Feb 3, 2021

there is a SDFPtr readFile(const std::string &) at parser.hh:105 that didn't get overloaded

I was trying to have one less overload by only overloading the signature that also contains an sdf::Error. I did the same for readString.

that sounds fine to me

Should we deprecate sdf::SDFPtr readFile(const std::string &_filename);?

I would be ok with that. It's not the safest API since it doesn't have any way to report errors

@scpeters
Copy link
Member

scpeters commented Feb 3, 2021

I've reported the windows compiler warning to the ign-utils repository gazebosim/gz-utils#9

Signed-off-by: Addisu Z. Taddese <addisu@openrobotics.org>
@azeey
Copy link
Collaborator Author

azeey commented Feb 3, 2021

@osrf-jenkins run tests please

Copy link
Member

@scpeters scpeters left a comment

Choose a reason for hiding this comment

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

this is looking good to me; I just have two small suggestions for extra test expectations


ASSERT_TRUE(sdf::ParserConfig::GlobalConfig().FindFileCallback());
EXPECT_EQ("test/dir2",
sdf::ParserConfig::GlobalConfig().FindFileCallback()("empty"));
Copy link
Member

Choose a reason for hiding this comment

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

how do you feel about adding some expectations about sdf::findFile?

diff --git a/test/integration/parser_config.cc b/test/integration/parser_config.cc
index d6bd2fb3..3f9780ac 100644
--- a/test/integration/parser_config.cc
+++ b/test/integration/parser_config.cc
@@ -51,6 +51,9 @@ TEST(ParserConfig, GlobalConfig)
   ASSERT_TRUE(sdf::ParserConfig::GlobalConfig().FindFileCallback());
   EXPECT_EQ("test/dir2",
       sdf::ParserConfig::GlobalConfig().FindFileCallback()("empty"));
+  // sdf::findFile requires explicitly enabling callbacks
+  EXPECT_EQ("test/dir2", sdf::findFile("empty", false, true));
+  EXPECT_EQ("test/dir2", sdf::findFile("empty", true, true));
 }
 
 /////////////////////////////////////////////////

EXPECT_EQ(it->second.front(), testDir);

ASSERT_TRUE(config.FindFileCallback());
EXPECT_EQ("test/dir2", config.FindFileCallback()("empty"));
Copy link
Member

Choose a reason for hiding this comment

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

how do you feel about calling sdf::findFile here as well?

diff --git a/test/integration/parser_config.cc b/test/integration/parser_config.cc
@@ -79,6 +82,8 @@ TEST(ParserConfig, NonGlobalConfig)
 
   ASSERT_TRUE(config.FindFileCallback());
   EXPECT_EQ("test/dir2", config.FindFileCallback()("empty"));
+  EXPECT_EQ("test/dir2", sdf::findFile("empty", false, true, config));
+  EXPECT_EQ("test/dir2", sdf::findFile("empty", true, true, config));
 
   EXPECT_TRUE(sdf::ParserConfig::GlobalConfig().URIPathMap().empty());
   EXPECT_FALSE(sdf::ParserConfig::GlobalConfig().FindFileCallback());

Signed-off-by: Addisu Z. Taddese <addisu@openrobotics.org>
@azeey
Copy link
Collaborator Author

azeey commented Feb 4, 2021

@osrf-jenkins run tests please

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🏢 edifice Ignition Edifice
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants