dotenv-linter icon indicating copy to clipboard operation
dotenv-linter copied to clipboard

Increase verbosity of "Unable to correctly parse dotenv file" error when violating IncorrectNameViolation

Open Levivb opened this issue 6 years ago • 4 comments

Bug report

What's wrong

The linter will return a rather not-helpful message "Unable to correctly parse dotenv file" when the dotenv file contains invalid characters in the name. For example:

;INCORRENT_COMMENT=disabled
SOME@KEY=incorrect

How is that should be

The linter should return something like:

.env:22 101 Found incorrect name: ;INCORRENT_COMMENT
.env:23 101 Found incorrect name: SOME@KEY

System information

Linux homestead 4.15.0-32-generic #35-Ubuntu SMP Fri Aug 10 17:58:07 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

flake8 information

n/a

pip information

not relevant

Levivb avatar Jan 12 '19 10:01 Levivb

@Levivb hm, thanks for the feedback.

However, I guess raising IncorrectNameViolation instead is not a good option. I would prefer just to raise something like this: Unable to correctly parse dotenv file, line: ";INCORRENT_COMMENT=disabled"

What do you think?

sobolevn avatar Jan 12 '19 10:01 sobolevn

That would be fine as well. From a user perspective it's still an invalid key, but I reckon that would cause conflicting behaviour since other violations are hard to check when the file can't be processed correctly in the first place.

In my case I had to remove all lines from the file and add them in batches until the error pops up. Your proposed solution would prevent that, so it sounds like a viable solution :)

Levivb avatar Jan 12 '19 10:01 Levivb

@Levivb are you interested in giving this a try with a PR? 🙂

sobolevn avatar Jan 12 '19 10:01 sobolevn

I'm afraid my python knowledge is not sufficient enough to get this done in a reasonable amount of time.

I checked the source code and tried a few things. But since I even have to google how to get the human readable message out of an exception... or even how to get a named exception in the first place (except ParsingError as pe: vs except ParsingError), I'll write some pseudo code for you to do with as you wish.

.env file contents:
KEY_0="KEY@2=sec(o)nd with error"
KEY_1=first
KEY@2=sec(o)nd with error
KEY_3=third
KEY_4=fourth

# In checker.py in def _prepare_fst:
try:
    return self._parser.parse(file_contents)
except ParsingError as pe:
   #pe = '@2=sec(o)nd\nKEY_3=third\nKEY_4=fourth\n'
   #get first violating line
    violating_line = str(pe).split('\n')[0]
   #violating_line == @2=sec(o)nd

#there are two options here:
1.
file_contents_array = file_contents.split('\n')
for each line in file_contents_array:
  check if line corresponds to regex ^\w* regex_escape(violating_line) $
    # explanation: (ignore the spaces in the regex. they are only for readability here)
    # the line should start ^
    #the exception doesn't show the first part of the violating line
    #  so our regex should take that into account by allowing \w character group before the violating character
   # this will prevent false positives like: KEY_0="KEY@2=sec(o)nd with error"
   #
   # the violating line should be escaped for the regex so any regex specific characters aren't parsed as such. In this case `sec(o)nd` would try to match `second` where the `o` would be in a match group. When escaping, it would match exactly `sec(o)nd`
   # the line should end $
   
   if regex matches, print the line with message it violates and break loop

2.
  find the index of the substring `(^|\\n) \w* regex_escape(violating_line) ($|\\n)`
  # ignore the spaces in the regex. they are only for readability here
  # explanation: find start of string or newline character followed by any alphanumeric character. Followed by the violating string, followed by the end of the line or the next newline from the .env file
  # normally you'd use \b as word boundary, but since the newlines in the file_contents are actually truly the \ and n character, this won't work

  create a substring from the file_contents from the found index to the nearest \n or \b and print that substring

    return None

Levivb avatar Jan 12 '19 12:01 Levivb