51 "M191", "M192", "M193", "M194", |
51 "M191", "M192", "M193", "M194", |
52 "M195", "M196", "M197", "M198", |
52 "M195", "M196", "M197", "M198", |
53 |
53 |
54 ## Dictionaries with sorted keys |
54 ## Dictionaries with sorted keys |
55 "M201", |
55 "M201", |
|
56 |
|
57 ## Naive datetime usage |
|
58 "M301", "M302", "M303", "M304", "M305", "M306", "M307", "M308", |
|
59 "M311", "M312", "M313", "M314", "M315", |
|
60 "M321", |
56 |
61 |
57 ## Bugbear |
62 ## Bugbear |
58 "M501", "M502", "M503", "M504", "M505", "M506", "M507", "M508", |
63 "M501", "M502", "M503", "M504", "M505", "M506", "M507", "M508", |
59 "M509", |
64 "M509", |
60 "M511", "M512", "M513", |
65 "M511", "M512", "M513", |
174 (self.__checkTuple, ("M811",)), |
179 (self.__checkTuple, ("M811",)), |
175 (self.__checkMutableDefault, ("M821", "M822")), |
180 (self.__checkMutableDefault, ("M821", "M822")), |
176 (self.__checkReturn, ("M831", "M832", "M833", "M834")), |
181 (self.__checkReturn, ("M831", "M832", "M833", "M834")), |
177 (self.__checkLineContinuation, ("M841",)), |
182 (self.__checkLineContinuation, ("M841",)), |
178 (self.__checkCommentedCode, ("M891",)), |
183 (self.__checkCommentedCode, ("M891",)), |
|
184 (self.__checkDateTime, ("M301", "M302", "M303", "M304", "M305", |
|
185 "M306", "M307", "M308", "M311", "M312", |
|
186 "M313", "M314", "M315", "M321")), |
179 ] |
187 ] |
180 |
188 |
181 self.__defaultArgs = { |
189 self.__defaultArgs = { |
182 "BuiltinsChecker": { |
190 "BuiltinsChecker": { |
183 "chr": ["unichr", ], |
191 "chr": ["unichr", ], |
253 else: |
261 else: |
254 offset = (1, 0) |
262 offset = (1, 0) |
255 self.__error(offset[0] - 1, offset[1] or 0, |
263 self.__error(offset[0] - 1, offset[1] or 0, |
256 'M901', exc_type.__name__, exc.args[0]) |
264 'M901', exc_type.__name__, exc.args[0]) |
257 |
265 |
258 def run(self): |
266 def __generateTree(self): |
259 """ |
267 """ |
260 Public method to check the given source against miscellaneous |
268 Private method to generate an AST for our source. |
261 conditions. |
269 |
262 """ |
270 @return generated AST |
263 if not self.__filename: |
271 @rtype ast.AST |
264 # don't do anything, if essential data is missing |
272 """ |
265 return |
|
266 |
|
267 if not self.__checkers: |
|
268 # don't do anything, if no codes were selected |
|
269 return |
|
270 |
|
271 source = "".join(self.__source) |
273 source = "".join(self.__source) |
272 # Check type for py2: if not str it's unicode |
274 # Check type for py2: if not str it's unicode |
273 if sys.version_info[0] == 2: |
275 if sys.version_info[0] == 2: |
274 try: |
276 try: |
275 source = source.encode('utf-8') |
277 source = source.encode('utf-8') |
276 except UnicodeError: |
278 except UnicodeError: |
277 pass |
279 pass |
|
280 |
|
281 return compile(source, self.__filename, 'exec', ast.PyCF_ONLY_AST) |
|
282 |
|
283 def run(self): |
|
284 """ |
|
285 Public method to check the given source against miscellaneous |
|
286 conditions. |
|
287 """ |
|
288 if not self.__filename: |
|
289 # don't do anything, if essential data is missing |
|
290 return |
|
291 |
|
292 if not self.__checkers: |
|
293 # don't do anything, if no codes were selected |
|
294 return |
|
295 |
278 try: |
296 try: |
279 self.__tree = compile(source, self.__filename, 'exec', |
297 self.__tree = self.__generateTree() |
280 ast.PyCF_ONLY_AST) |
|
281 except (SyntaxError, TypeError): |
298 except (SyntaxError, TypeError): |
282 self.__reportInvalidSyntax() |
299 self.__reportInvalidSyntax() |
283 return |
300 return |
284 |
301 |
285 for check in self.__checkers: |
302 for check in self.__checkers: |
1733 |
1772 |
1734 elif beforeAssign < lineno: |
1773 elif beforeAssign < lineno: |
1735 return True |
1774 return True |
1736 |
1775 |
1737 return False |
1776 return False |
|
1777 |
|
1778 |
|
1779 class DateTimeVisitor(ast.NodeVisitor): |
|
1780 """ |
|
1781 Class implementing a node visitor to check datetime function calls. |
|
1782 |
|
1783 Note: This class is modelled after flake8_datetimez checker. |
|
1784 """ |
|
1785 def __init__(self): |
|
1786 """ |
|
1787 Constructor |
|
1788 """ |
|
1789 super(DateTimeVisitor, self).__init__() |
|
1790 |
|
1791 self.violations = [] |
|
1792 |
|
1793 def __getFromKeywords(self, keywords, name): |
|
1794 """ |
|
1795 Private method to get a keyword node given its name. |
|
1796 |
|
1797 @param keywords list of keyword argument nodes |
|
1798 @type list of ast.AST |
|
1799 @param name name of the keyword node |
|
1800 @type str |
|
1801 @return keyword node |
|
1802 @rtype ast.AST |
|
1803 """ |
|
1804 for keyword in keywords: |
|
1805 if keyword.arg == name: |
|
1806 return keyword |
|
1807 |
|
1808 return None |
|
1809 |
|
1810 def visit_Call(self, node): |
|
1811 """ |
|
1812 Public method to handle a function call. |
|
1813 |
|
1814 Every datetime related function call is check for use of the naive |
|
1815 variant (i.e. use without TZ info). |
|
1816 |
|
1817 @param node reference to the node to be processed |
|
1818 @type ast.Call |
|
1819 """ |
|
1820 # datetime.something() |
|
1821 isDateTimeClass = ( |
|
1822 isinstance(node.func, ast.Attribute) and |
|
1823 isinstance(node.func.value, ast.Name) and |
|
1824 node.func.value.id == 'datetime') |
|
1825 |
|
1826 # datetime.datetime.something() |
|
1827 isDateTimeModuleAndClass = ( |
|
1828 isinstance(node.func, ast.Attribute) and |
|
1829 isinstance(node.func.value, ast.Attribute) and |
|
1830 node.func.value.attr == 'datetime' and |
|
1831 isinstance(node.func.value.value, ast.Name) and |
|
1832 node.func.value.value.id == 'datetime') |
|
1833 |
|
1834 if isDateTimeClass: |
|
1835 if node.func.attr == 'datetime': |
|
1836 # datetime.datetime(2000, 1, 1, 0, 0, 0, 0, |
|
1837 # datetime.timezone.utc) |
|
1838 isCase1 = (len(node.args) >= 8 and |
|
1839 not (isinstance(node.args[7], ast.NameConstant) and |
|
1840 node.args[7].value is None)) |
|
1841 |
|
1842 # datetime.datetime(2000, 1, 1, tzinfo=datetime.timezone.utc) |
|
1843 tzinfoKeyword = self.__getFromKeywords(node.keywords, 'tzinfo') |
|
1844 isCase2 = (tzinfoKeyword is not None and |
|
1845 not (isinstance(tzinfoKeyword.value, |
|
1846 ast.NameConstant) and |
|
1847 tzinfoKeyword.value.value is None)) |
|
1848 |
|
1849 if not (isCase1 or isCase2): |
|
1850 self.violations.append((node, "M301")) |
|
1851 |
|
1852 elif node.func.attr == 'time': |
|
1853 # time(12, 10, 45, 0, datetime.timezone.utc) |
|
1854 isCase1 = (len(node.args) >= 5 and |
|
1855 not (isinstance(node.args[4], ast.NameConstant) and |
|
1856 node.args[4].value is None)) |
|
1857 |
|
1858 # datetime.time(12, 10, 45, tzinfo=datetime.timezone.utc) |
|
1859 tzinfoKeyword = self.__getFromKeywords(node.keywords, 'tzinfo') |
|
1860 isCase2 = (tzinfoKeyword is not None and |
|
1861 not (isinstance(tzinfoKeyword.value, |
|
1862 ast.NameConstant) and |
|
1863 tzinfoKeyword.value.value is None)) |
|
1864 |
|
1865 if not (isCase1 or isCase2): |
|
1866 self.violations.append((node, "M321")) |
|
1867 |
|
1868 elif node.func.attr == 'date': |
|
1869 self.violations.append((node, "M311")) |
|
1870 |
|
1871 if isDateTimeClass or isDateTimeModuleAndClass: |
|
1872 if node.func.attr == 'today': |
|
1873 self.violations.append((node, "M302")) |
|
1874 |
|
1875 elif node.func.attr == 'utcnow': |
|
1876 self.violations.append((node, "M303")) |
|
1877 |
|
1878 elif node.func.attr == 'utcfromtimestamp': |
|
1879 self.violations.append((node, "M304")) |
|
1880 |
|
1881 elif node.func.attr in 'now': |
|
1882 # datetime.now(UTC) |
|
1883 isCase1 = (len(node.args) == 1 and |
|
1884 len(node.keywords) == 0 and |
|
1885 not (isinstance(node.args[0], ast.NameConstant) and |
|
1886 node.args[0].value is None)) |
|
1887 |
|
1888 # datetime.now(tz=UTC) |
|
1889 tzKeyword = self.__getFromKeywords(node.keywords, 'tz') |
|
1890 isCase2 = (tzKeyword is not None and |
|
1891 not (isinstance(tzKeyword.value, |
|
1892 ast.NameConstant) and |
|
1893 tzKeyword.value.value is None)) |
|
1894 |
|
1895 if not (isCase1 or isCase2): |
|
1896 self.violations.append((node, "M305")) |
|
1897 |
|
1898 elif node.func.attr == 'fromtimestamp': |
|
1899 # datetime.fromtimestamp(1234, UTC) |
|
1900 isCase1 = (len(node.args) == 2 and |
|
1901 len(node.keywords) == 0 and |
|
1902 not (isinstance(node.args[1], ast.NameConstant) and |
|
1903 node.args[1].value is None)) |
|
1904 |
|
1905 # datetime.fromtimestamp(1234, tz=UTC) |
|
1906 tzKeyword = self.__getFromKeywords(node.keywords, 'tz') |
|
1907 isCase2 = (tzKeyword is not None and |
|
1908 not (isinstance(tzKeyword.value, |
|
1909 ast.NameConstant) and |
|
1910 tzKeyword.value.value is None)) |
|
1911 |
|
1912 if not (isCase1 or isCase2): |
|
1913 self.violations.append((node, "M306")) |
|
1914 |
|
1915 elif node.func.attr == 'strptime': |
|
1916 # datetime.strptime(...).replace(tzinfo=UTC) |
|
1917 parent = getattr(node, '_dtCheckerParent', None) |
|
1918 pparent = getattr(parent, '_dtCheckerParent', None) |
|
1919 if not (isinstance(parent, ast.Attribute) and |
|
1920 parent.attr == 'replace'): |
|
1921 isCase1 = False |
|
1922 elif not isinstance(pparent, ast.Call): |
|
1923 isCase1 = False |
|
1924 else: |
|
1925 tzinfoKeyword = self.__getFromKeywords(pparent.keywords, |
|
1926 'tzinfo') |
|
1927 isCase1 = (tzinfoKeyword is not None and |
|
1928 not (isinstance(tzinfoKeyword.value, |
|
1929 ast.NameConstant) and |
|
1930 tzinfoKeyword.value.value is None)) |
|
1931 |
|
1932 if not isCase1: |
|
1933 self.violations.append((node, "M307")) |
|
1934 |
|
1935 elif node.func.attr == 'fromordinal': |
|
1936 self.violations.append((node, "M308")) |
|
1937 |
|
1938 # date.something() |
|
1939 isDateClass = (isinstance(node.func, ast.Attribute) and |
|
1940 isinstance(node.func.value, ast.Name) and |
|
1941 node.func.value.id == 'date') |
|
1942 |
|
1943 # datetime.date.something() |
|
1944 isDateModuleAndClass = (isinstance(node.func, ast.Attribute) and |
|
1945 isinstance(node.func.value, ast.Attribute) and |
|
1946 node.func.value.attr == 'date' and |
|
1947 isinstance(node.func.value.value, ast.Name) and |
|
1948 node.func.value.value.id == 'datetime') |
|
1949 |
|
1950 if isDateClass or isDateModuleAndClass: |
|
1951 if node.func.attr == 'today': |
|
1952 self.violations.append((node, "M312")) |
|
1953 |
|
1954 elif node.func.attr == 'fromtimestamp': |
|
1955 self.violations.append((node, "M313")) |
|
1956 |
|
1957 elif node.func.attr == 'fromordinal': |
|
1958 self.violations.append((node, "M314")) |
|
1959 |
|
1960 elif node.func.attr == 'fromisoformat': |
|
1961 self.violations.append((node, "M315")) |
|
1962 |
|
1963 self.generic_visit(node) |
|
1964 |
1738 # |
1965 # |
1739 # eflag: noqa = M702 |
1966 # eflag: noqa = M702 |