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

wxgui: Create grassdata automatically on the first GUI startup #705

Conversation

lindakarlovska
Copy link
Contributor

@lindakarlovska lindakarlovska commented Jun 12, 2020

After starting GRASS GIS with GUI, the GUI now searches for existing grassdata (e.g., dac6d4a and #644 with #664). If nothing is found, GRASS GIS (GUI) should automatically create directory named grassdata as a subdirectory of a platfrom-dependent directory. This platfrom-dependent directory would be:

  • $HOME (os.path.expanduser('~')) on Linux and the like,
  • User's Documents on Windows (see dac6d4a for code trying to identify that dir),
  • One of the above on macOS - macOS users, please share your ideas.

If that fails (GRASS is running in some special environment and the standard directories cannot be created which might be the case in some "try out GRASS" use cases) it should use a temporary directory (/tmp/... etc.) as a fallback.
The created tmp is not cleaned by GRASS, so we are relying on the system to do it at some point.

@lindakarlovska lindakarlovska changed the title New function for a grassdata directory in the default path Create grassdata automatically on the first GUI startup Jun 12, 2020
@lindakarlovska lindakarlovska marked this pull request as draft June 12, 2020 17:44
@petrasovaa petrasovaa added the gsoc Reserved for Google Summer of Code student(s) label Jun 13, 2020

try:
# here goes everything which has potential unicode issues
candidates.append(os.path.join(home, _("Documents"), "grassdata"))
Copy link
Member

Choose a reason for hiding this comment

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

Why _() in the path? Localized Windows should understand Documents.

Copy link
Member

Choose a reason for hiding this comment

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

This comes from dac6d4a - see the comment there. I would not bother with that for this function and just go with Documents only.

Copy link
Member

Choose a reason for hiding this comment

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

As I said, here in this new function, I would not bother with translations here as this is more future oriented and we hope that every new system will be set in a way that there is "Documents". So, I'm saying, delete the "candidates.append(os.path.join(home, _("Documents"), "grassdata"))" part.

Copy link
Member

@wenzeslaus wenzeslaus left a comment

Choose a reason for hiding this comment

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

The general structure is good, but there is a lot of smaller issues which need to be addressed. (See the individual comments.)

You can just go ahead and make macOS behave same as Linux as that's what @nilason suggests in #682.

Comment on lines 102 to 106
# If still doesn't exist, create "grassdata_username" dir in temp
if path is None:
os.mkdir(tmp)
path = tmp
return path
Copy link
Member

Choose a reason for hiding this comment

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

This goes against "If even [the temporary directory fallback] fails, GRASS should not fail, but simply let the user make that choice which is the current behavior." from #682.

Comment on lines 69 to 70
tmp = os.path.join(tempfile.gettempdir(),
"grassdata_{}".format(getpass.getuser()))
Copy link
Member

Choose a reason for hiding this comment

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

This should be created only once it is really needed. (Otherwise, the tmp dir will be created and not deleted every time thing function runs.)

try:
path = os.path.join(home, "grassdata")
os.mkdir(path)
except OSError:
Copy link
Member

Choose a reason for hiding this comment

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

The path is already set at this point even when exception was raised, so your line if path is None: will have path which is not None and thus the functions will just return this path.

path = os.path.join(home, "grassdata")
os.mkdir(path)
except OSError:
print ("Creation of the directory {} failed".format(path))
Copy link
Member

Choose a reason for hiding this comment

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

We may decide to refine it and give more info to the user through GUI, but at this point and in this function, there is no need to report the failure in any way. This function has a fallback which is the tmp dir and the original calling code has its own fallback solution.


try:
# here goes everything which has potential unicode issues
candidates.append(os.path.join(home, _("Documents"), "grassdata"))
Copy link
Member

Choose a reason for hiding this comment

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

This comes from dac6d4a - see the comment there. I would not bother with that for this function and just go with Documents only.

Comment on lines 522 to 531
else:
# nothing found
# TODO: should it be warning, hint or message?
self._showWarning(_(
'GRASS needs a directory (GRASS database) '
'in which to store its data. '
'Create one now if you have not already done so. '
'A popular choice is "grassdata", located in '
'your home directory. '
'Press Browse button to select the directory.'))
Copy link
Member

Choose a reason for hiding this comment

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

This still should be there. (#682: "If even [the temporary directory fallback] fails, [...] simply let the user make that choice which is the current behavior.")

Comment on lines 61 to 62
def create_possible_database_path():
"""Create the directory to what is possibly a GRASS Database.
Copy link
Member

Choose a reason for hiding this comment

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

possible -> standard, fix wording of "directory to what is"

def create_possible_database_path():
"""Create the directory to what is possibly a GRASS Database.

Create directory named grassdata in the usual locations.
Copy link
Member

Choose a reason for hiding this comment

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

Something like "...in the standard location according to the platform." And also mention the fallback.

@@ -530,6 +535,7 @@ def SuggestDatabase(self):
'your home directory. '
'Press Browse button to select the directory.'))


Copy link
Member

Choose a reason for hiding this comment

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

Please remove this extra change. Also in PEP8, there is only one empty line between methods.

@@ -507,6 +508,10 @@ def SuggestDatabase(self):
if self.GetRCValue("LOCATION_NAME") != "<UNKNOWN>":
return
path = get_possible_database_path()
# If nothing found, create GRASS directory
Copy link
Member

Choose a reason for hiding this comment

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

Based on what the function does and what the context is, the comment should be "...try to create..."

home = os.path.expanduser('~')
candidates = []

# Candidate for independent "grassdata" in for Linux and macOS
Copy link
Member

Choose a reason for hiding this comment

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

What the word "independent" refers to here?


try:
# here goes everything which has potential unicode issues
candidates.append(os.path.join(home, _("Documents"), "grassdata"))
Copy link
Member

Choose a reason for hiding this comment

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

As I said, here in this new function, I would not bother with translations here as this is more future oriented and we hope that every new system will be set in a way that there is "Documents". So, I'm saying, delete the "candidates.append(os.path.join(home, _("Documents"), "grassdata"))" part.

Comment on lines 90 to 91
path = candidate
return path
Copy link
Member

Choose a reason for hiding this comment

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

Here, you don't have to assign to a variable here, just return candidate right away.

Comment on lines 96 to 97
tmp = os.path.join(tempfile.gettempdir(),
"grassdata_{}".format(getpass.getuser()))
Copy link
Member

Choose a reason for hiding this comment

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

According to PEP8, t from tempfile and " from "grassdata... should be aligned. Another option is to use Black style which puts this into 4 lines if it is longer than 88 characters:

tmp = os.path.join(
    tempfile.gettempdir(),
    "grassdata_{}".format(getpass.getuser())
) 

You can run Flake8 on any file you edit to see these things. Unfortunately, you will get a lot of warning and errors from other lines you did not edit and you need to ignore those (this will get better in the future as we make more files PEP8, Flake8, and Black compliant).

else:
try:
os.mkdir(tmp)
path = tmp
Copy link
Member

Choose a reason for hiding this comment

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

Again, just return here is enough.

Comment on lines 101 to 102
path = tmp
return path
Copy link
Member

Choose a reason for hiding this comment

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

Only return is needed here, no assignment.

tmp = os.path.join(tempfile.gettempdir(),
"grassdata_{}".format(getpass.getuser()))

# Control if exists, if does not, create it
Copy link
Member

Choose a reason for hiding this comment

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

Control -> Check

@@ -55,6 +58,58 @@ def get_possible_database_path():
return path


def create_possible_database_path():
Copy link
Member

Choose a reason for hiding this comment

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

create_possible_database_path() -> create_database_directory()

The GRASS terminology here is all over the place here (grassdata, gisbase, ...), but "possible" does not make sense here and we are not creating a path, but the actual directory, so that's way "...database_directory".

Comment on lines 96 to 101
else:
try:
os.mkdir(tmp)
return tmp
except OSError:
pass
Copy link
Member

Choose a reason for hiding this comment

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

Since the true branch of the if statement has return, you don't need else here and can move the try-except block one level left. This is does not change anything, but is makes the code simpler and the same logic is already used above: The good case returns, so the other case is just whatever follows after that.

Comment on lines 72 to 77
# Candidate for case independent "grassdata" for Linux and macOS
if sys.platform.startswith('linux') or sys.platform.startswith('darwin'):
candidates.append(os.path.join(home, "grassdata"))
# Candidates for case independent "grassdata" in other dirs (Windows)
else:
candidates.append(os.path.join(home, "Documents", "grassdata"))
Copy link
Member

Choose a reason for hiding this comment

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

Simple candidate = os.path... will suffice here in both cases. It is either or and at one point you have only one candidate. This removes the need for the loop over the candidates list below.

@lindakarlovska lindakarlovska changed the title Create grassdata automatically on the first GUI startup wxgui: Create grassdata automatically on the first GUI startup Jun 19, 2020
Copy link
Member

@wenzeslaus wenzeslaus left a comment

Choose a reason for hiding this comment

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

Only minor comments to make the code more readable.

Comment on lines 71 to 74
# Candidate for case independent "grassdata" for Linux and macOS
if sys.platform.startswith('linux') or sys.platform.startswith('darwin'):
candidate = os.path.join(home, "grassdata")
# Candidates for case independent "grassdata" in other dirs (Windows)
Copy link
Member

Choose a reason for hiding this comment

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

The "case independent" does not make sense here. Simplify further, by merging the two comments together and saying something like "Determine the standard path according to a platform."

While you are at it, you can drop the "candidate" terminology from the variable name too. What candidate refers to here? It really makes sense only in the other function. Simple path will be probably more descriptive here. And going further, the tmp variable can be called path too because in a way it is no different from the path before, it replaces it for all intents and purposes.

"grassdata_{}".format(getpass.getuser())
)

# Check if exists, if does not, create it
Copy link
Member

Choose a reason for hiding this comment

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

The intended behavior may not be clear from the code, so add another comment saying something like "The created tmp is not cleaned by GRASS, so we are relying on the system to do it at some point. The positive outcome is that another GRASS instance will find the data created by the first one which is desired in the "try out GRASS" use case we are aiming towards." See the original ticket for alternative formulation of this.

except OSError:
pass

# Temporary "grassdata" directory
Copy link
Member

Choose a reason for hiding this comment

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

Say something like "Create a temporary "grassdata" directory if GRASS is running in some special environment and the standard directories cannot be created which might be the case in some "try out GRASS" use cases."

(Note that a line should be 88 characters or less.)

Copy link
Member

@wenzeslaus wenzeslaus left a comment

Choose a reason for hiding this comment

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

Catching trailing whitespace.

Use flake8 myfile.py to check the contents of files you edit. Ignore what is reported for lines you did not touch. Fix what is reported for lines you are changing. Use the https://www.flake8rules.com/ website for explanations. Ask if something is not clear. Not all the rules can be applied to the whole GRASS code base, but most of them should.

@@ -38,7 +38,8 @@
from core.gcmd import GMessage, GError, DecodeString, RunCommand
from core.utils import GetListOfLocations, GetListOfMapsets
from startup.utils import (
get_lockfile_if_present, get_possible_database_path, create_mapset)
get_lockfile_if_present, get_possible_database_path,
Copy link
Member

Choose a reason for hiding this comment

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

Trailing whitespace (Flake8 W291)

Comment on lines 64 to 66
Creates database directory named grassdata in the standard location
according to the platform.

Copy link
Member

Choose a reason for hiding this comment

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

Trailing whitespace (Flake8 W291)


Returns the new path as a string or None if nothing was found or created.
"""
home = os.path.expanduser('~')
Copy link
Member

Choose a reason for hiding this comment

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

Trailing whitespace (Flake8 W291)

if sys.platform.startswith('linux') or sys.platform.startswith('darwin'):
candidate = os.path.join(home, "grassdata")
# Candidates for case independent "grassdata" in other dirs (Windows)
else:
Copy link
Member

Choose a reason for hiding this comment

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

Trailing whitespace (Flake8 W291)

else:
candidate = os.path.join(home, "Documents", "grassdata")

# Create "grassdata" directory
Copy link
Member

Choose a reason for hiding this comment

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

Trailing whitespace (Flake8 W291)

except OSError:
pass

# Temporary "grassdata" directory
Copy link
Member

Choose a reason for hiding this comment

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

Trailing whitespace (Flake8 W291)

tmp = os.path.join(
tempfile.gettempdir(),
"grassdata_{}".format(getpass.getuser())
)
Copy link
Member

Choose a reason for hiding this comment

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

Trailing whitespace (Flake8 W291)

… 'path', edited description related to the temporary file.
home = os.path.expanduser('~')

# Determine the standard path according to the platform
if sys.platform.startswith('linux') or sys.platform.startswith('darwin'):
Copy link
Contributor

Choose a reason for hiding this comment

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

Hopefully the last change, sorry I missed this before: there are other platforms (see https://stackoverflow.com/questions/446209/possible-values-from-sys-platform) so we should probably have it more like this (and treat windows as special case):

if sys.platform == 'win32':
    path = os.path.join(home, "Documents", "grassdata")
else:
    path = os.path.join(home, "grassdata")

@petrasovaa petrasovaa marked this pull request as ready for review June 21, 2020 03:13
@wenzeslaus wenzeslaus merged commit 4b6d5f4 into OSGeo:master Jun 23, 2020
@neteler neteler added this to the 8.0.0 milestone Dec 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
gsoc Reserved for Google Summer of Code student(s)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants