Skip to content

bpo-29812: Improve testing of token and tokenize#681

Closed
ammaraskar wants to merge 4 commits into
python:masterfrom
ammaraskar:master
Closed

bpo-29812: Improve testing of token and tokenize#681
ammaraskar wants to merge 4 commits into
python:masterfrom
ammaraskar:master

Conversation

@ammaraskar

Copy link
Copy Markdown
Member

Adds a token.py generation test, and a cross-check between the token and tokenize modules.

The generation test is akin to the current automated file generator tests in test_keyword and test_symbol. The newly added check in tokenize ensures that all the literal tokens in token such as ':', '...', '@=' have a corresponding entry within the EXACT_TOKEN_TYPES dict in tokenize.

@mention-bot

Copy link
Copy Markdown

@ammaraskar, thanks for your PR! By analyzing the history of the files in this pull request, we identified @serhiy-storchaka, @tiran and @1st1 to be potential reviewers.

@ammaraskar
ammaraskar force-pushed the master branch 2 times, most recently from 5c9e66a to dbce4b5 Compare March 16, 2017 02:49
@ammaraskar

Copy link
Copy Markdown
Member Author

Weirdly enough this fails on AppVeyor (which is Windows, I believe). I'm not sure if this is something wrong with the test or an actual problem on windows, I'll look into it tomorrow when I have access to a windows computer.

Comment thread Lib/test/test_token.py Outdated
@@ -0,0 +1,52 @@
import token
import unittest
from test import support

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please group stdlib modules together and sort them alphabetically.

Comment thread Lib/test/test_token.py Outdated
import sys
import subprocess

TOKEN_FILE = support.findfile('token.py')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Style nit: Don't add additional spaces before =.

Comment thread Lib/test/test_token.py Outdated
rc, stderr = self._generate_tokens(os.devnull, NONEXISTENT_FILE)
self.assertNotEqual(rc, 0)
self.assertIn(b'I/O error', stderr)
self.assertIn(bytes(NONEXISTENT_FILE, encoding='ascii'), stderr)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

NONEXISTENT_FILE doesn't contain any non-ASCII chars. Do we really need encoding='ascii'?

Also, I prefer to have a nonexistent_file local variable in the test instead of a constant.

@ammaraskar ammaraskar Apr 11, 2017

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The encoding is required when passing a string in python3: https://docs.python.org/3/library/functions.html#bytes
https://docs.python.org/3/library/functions.html#bytearray

The reason its a b'string' is because it needs to be passed as an argument to Popen, changing it to a local might result in an easier fix for this though.

Comment thread Lib/test/test_token.py Outdated
self.assertIn(bytes(NONEXISTENT_FILE, encoding='ascii'), stderr)


if __name__ == "__main__":

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Style use: Please use single quotes where possible.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Will do, for reference, this was copied from https://github.com/python/cpython/blob/master/Lib/test/test_keyword.py

Comment thread Lib/test/test_token.py Outdated
stderr = proc.communicate()[1]
return proc.returncode, stderr

@unittest.skipIf(not os.path.exists(TOKEN_FILE),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd replace

@unittest.skipIf(not condition)

with

@unittest.skipUnless(condition)

Comment thread Lib/test/test_token.py Outdated
'test only works from source build directory')
def test_real_token_file(self):
self._copy_file_without_generated_tokens(TOKEN_FILE, TEST_PY_FILE)
self.addCleanup(support.unlink, TEST_PY_FILE)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You can probably move the addCleanup call to inside of _copy_file_without_generated_tokens method.

Comment thread Lib/test/test_token.py Outdated
TEST_PY_FILE))
self.assertTrue(filecmp.cmp(TOKEN_FILE, TEST_PY_FILE))

def test_missing_output_file_causes_Error(self):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Error -> error or exception

Comment thread Lib/test/test_tokenize.py Outdated
token.tok_name[token.ENDMARKER])

def test_all_literal_tokens(self):
NON_LITERALS = {token.ENDMARKER, token.NAME, token.NUMBER,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

NON_LITERALS -> non_literals or non_literal_tokens

Comment thread Lib/test/test_tokenize.py Outdated
token.tok_name[token.ENDMARKER])

def test_all_literal_tokens(self):
NON_LITERALS = {token.ENDMARKER, token.NAME, token.NUMBER,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also, I find hanging indents much more readable and they will make future diffs less noisy:

non_literal_tokens = {
    token.STRING, token.NEWLINE, token.INDENT,
    ...,
    # You can add another line without having to modify the '}' so diffs will be less noisy.
    token.N_TOKENS, token.NT_OFFSET,
}

Comment thread Lib/test/test_tokenize.py Outdated
continue
name = tok_name[tok]
self.assertIn(tok, EXACT_TOKEN_TYPES.values(),
"Literal token " + name + " not in EXACT_TOKEN_TYPES")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is OK, but an alternative solution would be to use self.subTest().

@ammaraskar
ammaraskar force-pushed the master branch 3 times, most recently from 963035d to 9b357ae Compare April 11, 2017 20:26
@ammaraskar

ammaraskar commented Apr 11, 2017

Copy link
Copy Markdown
Member Author

Oh also, I tried to look into what was calling the AppVeyor failure when comparing the files. From what I can tell its because os.stat is returning an incorrect value for st_size on the test environment or something https://ci.appveyor.com/project/ammaraskar/cpython/build/1.0.9#L3949

Doing a file content comparison causes it to pass.

Nevermind, I don't think this is an incorrect return since the offset of the size is exactly the number of lines, I think this has to do with line endings.

Adds a token.py generation test, and a cross-check between the
token and tokenize modules.

The generation test is akin to the current automated file generator tests
in test_keyword and test_symbol. The newly added check in tokenize ensures
that all the literal tokens in token such as ':', '...', '@=' have a
corresponding entry within the EXACT_TOKEN_TYPES dict in tokenize.
Currently this code does not work on windows
when the skeleton file has unix line endings
because fp.write('\n') will have the LF
changed to CRLF due to python's newline
interpolation.
@ammaraskar

Copy link
Copy Markdown
Member Author

Hey @berkerpeksag, could you take a look at this, I updated it fixing the concerns in your review.

@bitdancer could I also get a review from you seeing as you made the issue on the tracker for this?

@berkerpeksag berkerpeksag left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sorry for my late response. I left some comments. Please add a NEWS entry in Misc/NEWS.d/next/Tests.

Out of curiosity, how did you notice 3d4a043? It looks like AppVeyor didn't catch it.

Comment thread Lib/test/test_token.py
import os
import subprocess
import sys
from test import support

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please split stdlib and non-stdlib modules.

Comment thread Lib/test/test_token.py
self.addCleanup(support.unlink, dest_file)

def _generate_tokens(self, skeleton_file, target_token_py_file):
proc = subprocess.Popen([sys.executable,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can't we use the new subprocess.run() interface?

Comment thread Lib/token.py
with fp:
format = fp.read().split("\n")
format = fp.readlines()
nl = format[0][len(format[0].strip()):] if format else '\n'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd use more descriptive name like newline.

Comment thread Lib/test/test_tokenize.py
continue
name = tok_name[tok]
with self.subTest(name=name):
self.assertIn(tok, EXACT_TOKEN_TYPES.values())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looking at CI logs of an earlier commit, I wonder if we should make the following message clearer:

======================================================================
FAIL: test_all_literal_tokens (test.test_tokenize.TestTokenize) (name='NL')
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/travis/build/python/cpython/Lib/test/test_tokenize.py", line 1368, in test_all_literal_tokens
    self.assertIn(tok, EXACT_TOKEN_TYPES.values())
AssertionError: 58 not found in dict_values([7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 52, 51, 49, 50])

@ammaraskar

Copy link
Copy Markdown
Member Author

Thanks for your review @berkerpeksag. Closing this as per discussion on bpo.

@ammaraskar ammaraskar closed this Jul 12, 2017
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.

4 participants