diff --git a/.vs/ML9/v16/.suo b/.vs/ML9/v16/.suo index ac3d5127c12d8d4c2b70b3525e1b9ff46b2cf58b..a5d24894595a87643cc3365cb9d5a1de64041e52 100644 Binary files a/.vs/ML9/v16/.suo and b/.vs/ML9/v16/.suo differ diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite index a0e6f4d8286839ef7d549b25b127627e8dbbd7de..e15795308276cecf38a1782faf1038db48eca292 100644 Binary files a/.vs/slnx.sqlite and b/.vs/slnx.sqlite differ diff --git a/.vscode/.ropeproject/config.py b/.vscode/.ropeproject/config.py new file mode 100644 index 0000000000000000000000000000000000000000..dee2d1ae9a6be9cf0248130b6c6b9e2668052079 --- /dev/null +++ b/.vscode/.ropeproject/config.py @@ -0,0 +1,114 @@ +# The default ``config.py`` +# flake8: noqa + + +def set_prefs(prefs): + """This function is called before opening the project""" + + # Specify which files and folders to ignore in the project. + # Changes to ignored resources are not added to the history and + # VCSs. Also they are not returned in `Project.get_files()`. + # Note that ``?`` and ``*`` match all characters but slashes. + # '*.pyc': matches 'test.pyc' and 'pkg/test.pyc' + # 'mod*.pyc': matches 'test/mod1.pyc' but not 'mod/1.pyc' + # '.svn': matches 'pkg/.svn' and all of its children + # 'build/*.o': matches 'build/lib.o' but not 'build/sub/lib.o' + # 'build//*.o': matches 'build/lib.o' and 'build/sub/lib.o' + prefs['ignored_resources'] = ['*.pyc', '*~', '.ropeproject', + '.hg', '.svn', '_svn', '.git', '.tox'] + + # Specifies which files should be considered python files. It is + # useful when you have scripts inside your project. Only files + # ending with ``.py`` are considered to be python files by + # default. + # prefs['python_files'] = ['*.py'] + + # Custom source folders: By default rope searches the project + # for finding source folders (folders that should be searched + # for finding modules). You can add paths to that list. Note + # that rope guesses project source folders correctly most of the + # time; use this if you have any problems. + # The folders should be relative to project root and use '/' for + # separating folders regardless of the platform rope is running on. + # 'src/my_source_folder' for instance. + # prefs.add('source_folders', 'src') + + # You can extend python path for looking up modules + # prefs.add('python_path', '~/python/') + + # Should rope save object information or not. + prefs['save_objectdb'] = True + prefs['compress_objectdb'] = False + + # If `True`, rope analyzes each module when it is being saved. + prefs['automatic_soa'] = True + # The depth of calls to follow in static object analysis + prefs['soa_followed_calls'] = 0 + + # If `False` when running modules or unit tests "dynamic object + # analysis" is turned off. This makes them much faster. + prefs['perform_doa'] = True + + # Rope can check the validity of its object DB when running. + prefs['validate_objectdb'] = True + + # How many undos to hold? + prefs['max_history_items'] = 32 + + # Shows whether to save history across sessions. + prefs['save_history'] = True + prefs['compress_history'] = False + + # Set the number spaces used for indenting. According to + # :PEP:`8`, it is best to use 4 spaces. Since most of rope's + # unit-tests use 4 spaces it is more reliable, too. + prefs['indent_size'] = 4 + + # Builtin and c-extension modules that are allowed to be imported + # and inspected by rope. + prefs['extension_modules'] = [] + + # Add all standard c-extensions to extension_modules list. + prefs['import_dynload_stdmods'] = True + + # If `True` modules with syntax errors are considered to be empty. + # The default value is `False`; When `False` syntax errors raise + # `rope.base.exceptions.ModuleSyntaxError` exception. + prefs['ignore_syntax_errors'] = False + + # If `True`, rope ignores unresolvable imports. Otherwise, they + # appear in the importing namespace. + prefs['ignore_bad_imports'] = False + + # If `True`, rope will insert new module imports as + # `from <package> import <module>` by default. + prefs['prefer_module_from_imports'] = False + + # If `True`, rope will transform a comma list of imports into + # multiple separate import statements when organizing + # imports. + prefs['split_imports'] = False + + # If `True`, rope will remove all top-level import statements and + # reinsert them at the top of the module when making changes. + prefs['pull_imports_to_top'] = True + + # If `True`, rope will sort imports alphabetically by module name instead + # of alphabetically by import statement, with from imports after normal + # imports. + prefs['sort_imports_alphabetically'] = False + + # Location of implementation of + # rope.base.oi.type_hinting.interfaces.ITypeHintingFactory In general + # case, you don't have to change this value, unless you're an rope expert. + # Change this value to inject you own implementations of interfaces + # listed in module rope.base.oi.type_hinting.providers.interfaces + # For example, you can add you own providers for Django Models, or disable + # the search type-hinting in a class hierarchy, etc. + prefs['type_hinting_factory'] = ( + 'rope.base.oi.type_hinting.factory.default_type_hinting_factory') + + +def project_opened(project): + """This function is called after opening the project""" + # Do whatever you like here! diff --git a/.vscode/.ropeproject/objectdb b/.vscode/.ropeproject/objectdb new file mode 100644 index 0000000000000000000000000000000000000000..0a47446c0ad231c193bdd44ff327ba2ab28bf3d8 Binary files /dev/null and b/.vscode/.ropeproject/objectdb differ diff --git a/.vscode/settings.json b/.vscode/settings.json index af5ad5b7a0df4f6e1d26951a92f015b4c491b83d..a9f5e278abb6e85f83fd1995e129b0ccb4472326 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "python.pythonPath": "d:\\ProjetsGit\\ML9\\venv\\Scripts\\python.exe" + "python.pythonPath": "d:\\ProjetsGit\\ML9\\venv\\Scripts\\python.exe", + "git.ignoreLimitWarning": true } \ No newline at end of file diff --git a/__pycache__/features_extractor.cpython-37.pyc b/__pycache__/features_extractor.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1196aa739c171bd81e7dfac4fdc61647d8d68557 Binary files /dev/null and b/__pycache__/features_extractor.cpython-37.pyc differ diff --git a/data_viz.py b/data_viz.py new file mode 100644 index 0000000000000000000000000000000000000000..be3ace60ed2c92a6d26cf1ef1109cd0c37b449c2 --- /dev/null +++ b/data_viz.py @@ -0,0 +1,13 @@ +import pandas as pd +import matplotlib.pyplot as plt + +csv_paths = ['train.csv', 'train_legacy.csv'] +data_list = [] +for csv_path in csv_paths: + data_list += [pd.read_csv(csv_path, index_col=0)] + +train_df = pd.concat(data_list, axis=0) +train_df.fillna('NA', inplace=True) + +train_df[['chars_in_subject']].plot() +plt.show() \ No newline at end of file diff --git a/features_extractor.py b/features_extractor.py new file mode 100644 index 0000000000000000000000000000000000000000..8258824788e8ae76f18582839b84127a8540819d --- /dev/null +++ b/features_extractor.py @@ -0,0 +1,196 @@ +import pandas as pd +import numpy as np +import xgboost as xgb +from copy import copy + +from sklearn.preprocessing import OneHotEncoder, MinMaxScaler, StandardScaler +from sklearn.discriminant_analysis import LinearDiscriminantAnalysis +from sklearn.decomposition import LatentDirichletAllocation +from sklearn.model_selection import cross_val_predict, train_test_split +from sklearn.cluster import MiniBatchKMeans + + +class FeatureExtractor(): + + def __init__(self, csv_paths, test_size=None): + + data_list = [] + for csv_path in csv_paths: + data_list += [pd.read_csv(csv_path, index_col=0)] + + self.train_df = pd.concat(data_list, axis=0) + self.train_df.fillna('NA', inplace=True) + + self.return_test = False + if test_size is not None: + self.return_test = True + self.train_df, self.test_df = train_test_split(self.train_df, test_size=test_size, random_state=42, shuffle=True) + self.y_test = np.ravel(self.test_df[['label']]) + + self.y_true = np.ravel(self.train_df[['label']]) + + sample_per_class = np.sum(OneHotEncoder(sparse=False).fit_transform(self.train_df[['label']].to_numpy()), axis=0) + print(sample_per_class/np.sum(sample_per_class)) + + @staticmethod + def _timestampToMonthDayHour(dataframe, timestampcolumn): + + def filter(timestamp, filter_type): + stamplist = timestamp.split(' ') + if len(stamplist) == 8: + stamplist = stamplist[:1] + stamplist[2:-1] + day, month, hour = stamplist[0][:-1], stamplist[-4], stamplist[-2][:2] + if len(stamplist) == 7: + stamplist = stamplist[:-1] + day, month, hour = stamplist[0][:-1], stamplist[-4], stamplist[-2][:2] + elif len(stamplist) == 2: + day, month, hour = 'NA', stamplist[0][3:6], stamplist[1][:2] + elif len(stamplist) == 5: + day, month, hour = 'NA', stamplist[-4], stamplist[-2][:2] + elif len(stamplist) == 6: + day, month, hour = stamplist[0][:-1], stamplist[-4], stamplist[-2][:2] + else: + day, month, hour = 'NA', 'NA', 'NA' + if day is None or month is None or hour is None: + day, month, hour = 'NA', 'NA', 'NA' + if filter_type == 'hour': + return hour + else: + return month + + dataframe['hour'] = timestampcolumn.apply(lambda timestamp: filter(timestamp, 'hour')) + dataframe['month'] = timestampcolumn.apply(lambda timestamp: filter(timestamp, 'month')) + + def extract_features(self, true_test_path, simple_categorical_variables=[], complex_categorical_variables=[], numerical_variables=[], numerical_variables_to_clusterise=[], time_variable=None, specific_class=None): + + true_test_df = pd.read_csv(true_test_path, index_col=0) + true_test_df.fillna('NA', inplace=True) + + x_train_list = [] + x_true_test_list = [] + features_labels = [] + + if len(simple_categorical_variables) > 0: + train_x_simple_categorical = self.train_df[simple_categorical_variables].to_numpy() + true_test_x_simple_categorical = true_test_df[simple_categorical_variables].to_numpy() + + feat_enc_categorical = OneHotEncoder(handle_unknown='ignore', sparse=False) + + train_x_simple_categorical = feat_enc_categorical.fit_transform(train_x_simple_categorical) + true_test_x_simple_categorical = feat_enc_categorical.transform(true_test_x_simple_categorical) + + x_train_list.append(train_x_simple_categorical) + x_true_test_list.append(true_test_x_simple_categorical) + + n_values = [2]*len(simple_categorical_variables) + variable_repetitions = [[simple_categorical_variables[i]]*n_values[i] for i in range(len(simple_categorical_variables))] + for variable_repetition in variable_repetitions: + features_labels += variable_repetition + + if specific_class is not None: + self.y_true = np.ravel(1-(self.train_df[['label']]==specific_class)) + + if time_variable is not None: + self._timestampToMonthDayHour(self.train_df, self.train_df[time_variable]) + self._timestampToMonthDayHour(true_test_df, true_test_df[time_variable]) + complex_categorical_variables += ['hour'] + complex_categorical_variables += ['month'] + + + if len(complex_categorical_variables) > 0: + features_encoders = {} + ldas = {} + for variable in complex_categorical_variables: + train_x_var = self.train_df[[variable]].to_numpy() + true_test_x_var = true_test_df[[variable]].to_numpy() + + feat_enc = OneHotEncoder(handle_unknown='ignore', sparse=False) + train_x_var = feat_enc.fit_transform(train_x_var) + true_test_x_var = feat_enc.transform(true_test_x_var) + features_encoders[variable] = copy(feat_enc) + + lda = LinearDiscriminantAnalysis() + # lda = LatentDirichletAllocation(n_components=4) + train_x_var = lda.fit_transform(train_x_var, self.y_true) + true_test_x_var = lda.transform(true_test_x_var) + ldas[variable] = copy(lda) + + x_train_list.append(train_x_var) + x_true_test_list.append(true_test_x_var) + features_labels += [variable]*train_x_var.shape[1] + + if len(numerical_variables) > 0: + features_encoders_num = {} + clusters = {} + for variable in numerical_variables: + train_x_numerical = self.train_df[[variable]].to_numpy() + true_test_x_var = true_test_df[[variable]].to_numpy() + + feat_enc_numerical = StandardScaler() + train_x_numerical = feat_enc_numerical.fit_transform(train_x_numerical) + true_test_x_var = feat_enc_numerical.transform(true_test_x_var) + features_encoders_num[variable] = copy(feat_enc_numerical) + + x_train_list.append(train_x_numerical) + x_true_test_list.append(true_test_x_var) + + if variable in numerical_variables_to_clusterise: + cluster = MiniBatchKMeans(n_clusters=4, max_iter=100, batch_size=100) + train_x_numerical = cluster.fit_transform(train_x_numerical) + true_test_x_var = cluster.transform(true_test_x_var) + clusters[variable] = copy(cluster) + + x_train_list.append(train_x_numerical) + x_true_test_list.append(true_test_x_var) + + features_labels += numerical_variables + + X_train = np.concatenate(x_train_list, axis=1) + print('n_features: {}'.format(X_train.shape)) + Y_train = self.y_true + + X_true_test = np.concatenate(x_true_test_list, axis=1) + + if self.return_test: + x_test_list = [] + if len(simple_categorical_variables) > 0: + + test_x_simple_categorical = self.test_df[simple_categorical_variables].to_numpy() + test_x_simple_categorical = feat_enc_categorical.transform(test_x_simple_categorical) + x_test_list.append(test_x_simple_categorical) + + if specific_class is not None: + self.y_test = np.ravel(1-(self.test_df[['label']]==specific_class)) + + if time_variable is not None: + self._timestampToMonthDayHour(self.test_df, self.test_df[time_variable]) + + if len(complex_categorical_variables) > 0: + + for variable in complex_categorical_variables: + + test_x_var = self.test_df[[variable]].to_numpy() + test_x_var = features_encoders[variable].transform(test_x_var) + test_x_var = ldas[variable].transform(test_x_var) + + x_test_list.append(test_x_var) + + if len(numerical_variables) > 0: + + for variable in numerical_variables: + test_x_var = self.test_df[[variable]].to_numpy() + test_x_var = features_encoders_num[variable].transform(test_x_var) + + x_test_list.append(test_x_var) + + if variable in numerical_variables_to_clusterise: + test_x_var = clusters[variable].transform(test_x_var) + x_test_list.append(test_x_var) + + X_test = np.concatenate(x_test_list, axis=1) + Y_test = self.y_test + + return X_train, Y_train, X_test, Y_test, X_true_test + + return X_train, Y_train, X_true_test + diff --git a/med_sample_submission.csv b/med_sample_submission.csv new file mode 100644 index 0000000000000000000000000000000000000000..a32fe5d9418fc87648bd5e7067d7b1f3ee39b49b --- /dev/null +++ b/med_sample_submission.csv @@ -0,0 +1,10746 @@ +Id,label +0,2 +1,0 +2,0 +3,3 +4,0 +5,0 +6,0 +7,2 +8,2 +9,0 +10,2 +11,0 +12,0 +13,0 +14,3 +15,2 +16,1 +17,0 +18,1 +19,1 +20,0 +21,0 +22,1 +23,0 +24,3 +25,3 +26,1 +27,0 +28,1 +29,2 +30,0 +31,1 +32,0 +33,3 +34,0 +35,0 +36,0 +37,2 +38,3 +39,0 +40,0 +41,1 +42,0 +43,0 +44,1 +45,0 +46,3 +47,3 +48,3 +49,3 +50,2 +51,0 +52,3 +53,2 +54,0 +55,0 +56,3 +57,2 +58,1 +59,3 +60,0 +61,3 +62,2 +63,3 +64,3 +65,0 +66,3 +67,0 +68,0 +69,0 +70,1 +71,0 +72,2 +73,2 +74,3 +75,2 +76,0 +77,2 +78,2 +79,2 +80,1 +81,0 +82,0 +83,0 +84,2 +85,0 +86,2 +87,0 +88,2 +89,2 +90,3 +91,3 +92,3 +93,3 +94,0 +95,0 +96,0 +97,0 +98,2 +99,0 +100,2 +101,2 +102,2 +103,0 +104,3 +105,0 +106,2 +107,0 +108,0 +109,0 +110,1 +111,0 +112,0 +113,3 +114,1 +115,3 +116,2 +117,2 +118,3 +119,0 +120,3 +121,2 +122,0 +123,0 +124,3 +125,1 +126,3 +127,0 +128,1 +129,1 +130,0 +131,1 +132,2 +133,0 +134,1 +135,1 +136,3 +137,0 +138,0 +139,0 +140,0 +141,2 +142,0 +143,2 +144,2 +145,1 +146,2 +147,0 +148,0 +149,3 +150,2 +151,3 +152,3 +153,0 +154,2 +155,3 +156,3 +157,2 +158,0 +159,1 +160,1 +161,1 +162,2 +163,3 +164,0 +165,0 +166,0 +167,0 +168,0 +169,0 +170,0 +171,0 +172,0 +173,3 +174,0 +175,0 +176,0 +177,3 +178,3 +179,0 +180,3 +181,0 +182,3 +183,0 +184,0 +185,2 +186,0 +187,3 +188,0 +189,3 +190,2 +191,1 +192,0 +193,0 +194,3 +195,1 +196,3 +197,0 +198,3 +199,3 +200,1 +201,0 +202,0 +203,3 +204,0 +205,3 +206,2 +207,0 +208,2 +209,0 +210,0 +211,3 +212,3 +213,3 +214,3 +215,3 +216,3 +217,2 +218,3 +219,2 +220,0 +221,0 +222,2 +223,0 +224,3 +225,2 +226,1 +227,3 +228,0 +229,0 +230,3 +231,0 +232,0 +233,2 +234,2 +235,1 +236,0 +237,3 +238,0 +239,0 +240,0 +241,0 +242,2 +243,0 +244,0 +245,0 +246,2 +247,0 +248,0 +249,0 +250,0 +251,0 +252,0 +253,0 +254,0 +255,0 +256,3 +257,0 +258,0 +259,2 +260,0 +261,0 +262,2 +263,2 +264,0 +265,0 +266,2 +267,3 +268,2 +269,2 +270,0 +271,0 +272,0 +273,0 +274,0 +275,0 +276,3 +277,0 +278,3 +279,0 +280,1 +281,1 +282,0 +283,0 +284,2 +285,3 +286,3 +287,0 +288,0 +289,2 +290,2 +291,0 +292,3 +293,0 +294,2 +295,0 +296,2 +297,3 +298,0 +299,1 +300,1 +301,0 +302,3 +303,1 +304,0 +305,2 +306,0 +307,0 +308,0 +309,0 +310,0 +311,2 +312,2 +313,2 +314,1 +315,0 +316,1 +317,2 +318,0 +319,0 +320,3 +321,1 +322,3 +323,0 +324,3 +325,2 +326,2 +327,2 +328,3 +329,2 +330,0 +331,0 +332,3 +333,1 +334,0 +335,0 +336,2 +337,1 +338,1 +339,0 +340,0 +341,0 +342,1 +343,0 +344,0 +345,0 +346,0 +347,0 +348,3 +349,3 +350,0 +351,3 +352,1 +353,1 +354,1 +355,0 +356,2 +357,2 +358,0 +359,0 +360,0 +361,3 +362,0 +363,1 +364,3 +365,0 +366,3 +367,1 +368,0 +369,3 +370,3 +371,0 +372,2 +373,0 +374,1 +375,3 +376,0 +377,3 +378,0 +379,3 +380,0 +381,0 +382,1 +383,0 +384,3 +385,1 +386,2 +387,1 +388,1 +389,0 +390,2 +391,3 +392,2 +393,3 +394,0 +395,2 +396,0 +397,0 +398,0 +399,3 +400,0 +401,2 +402,1 +403,1 +404,0 +405,3 +406,0 +407,0 +408,0 +409,1 +410,2 +411,0 +412,3 +413,0 +414,3 +415,3 +416,3 +417,0 +418,3 +419,0 +420,0 +421,2 +422,2 +423,1 +424,3 +425,0 +426,1 +427,2 +428,2 +429,3 +430,3 +431,2 +432,2 +433,1 +434,1 +435,2 +436,0 +437,2 +438,3 +439,0 +440,3 +441,2 +442,2 +443,2 +444,0 +445,2 +446,2 +447,3 +448,0 +449,0 +450,1 +451,2 +452,3 +453,0 +454,1 +455,1 +456,0 +457,0 +458,1 +459,3 +460,0 +461,0 +462,3 +463,0 +464,3 +465,2 +466,0 +467,3 +468,0 +469,0 +470,2 +471,2 +472,0 +473,0 +474,1 +475,1 +476,0 +477,3 +478,2 +479,2 +480,0 +481,0 +482,0 +483,2 +484,3 +485,3 +486,0 +487,0 +488,3 +489,1 +490,0 +491,0 +492,3 +493,3 +494,0 +495,3 +496,0 +497,2 +498,2 +499,0 +500,3 +501,0 +502,3 +503,2 +504,3 +505,0 +506,0 +507,2 +508,0 +509,0 +510,0 +511,0 +512,3 +513,0 +514,3 +515,0 +516,0 +517,3 +518,3 +519,0 +520,0 +521,0 +522,0 +523,3 +524,2 +525,0 +526,3 +527,0 +528,0 +529,0 +530,0 +531,1 +532,0 +533,0 +534,0 +535,0 +536,2 +537,3 +538,1 +539,0 +540,1 +541,3 +542,0 +543,3 +544,0 +545,2 +546,0 +547,3 +548,0 +549,3 +550,0 +551,0 +552,3 +553,0 +554,0 +555,0 +556,1 +557,0 +558,1 +559,0 +560,0 +561,2 +562,3 +563,0 +564,0 +565,0 +566,2 +567,2 +568,0 +569,3 +570,1 +571,0 +572,3 +573,0 +574,0 +575,0 +576,0 +577,3 +578,3 +579,0 +580,0 +581,0 +582,0 +583,0 +584,0 +585,3 +586,0 +587,0 +588,0 +589,1 +590,2 +591,0 +592,0 +593,2 +594,0 +595,3 +596,0 +597,1 +598,0 +599,3 +600,3 +601,0 +602,0 +603,0 +604,2 +605,0 +606,1 +607,1 +608,1 +609,1 +610,2 +611,0 +612,2 +613,0 +614,1 +615,2 +616,2 +617,0 +618,2 +619,0 +620,0 +621,2 +622,2 +623,1 +624,0 +625,0 +626,2 +627,0 +628,3 +629,3 +630,3 +631,2 +632,0 +633,1 +634,0 +635,2 +636,0 +637,3 +638,3 +639,0 +640,0 +641,2 +642,0 +643,0 +644,0 +645,3 +646,0 +647,2 +648,1 +649,1 +650,0 +651,3 +652,1 +653,0 +654,2 +655,0 +656,0 +657,2 +658,0 +659,0 +660,0 +661,0 +662,3 +663,3 +664,1 +665,3 +666,3 +667,1 +668,2 +669,0 +670,2 +671,2 +672,3 +673,3 +674,0 +675,0 +676,3 +677,0 +678,3 +679,0 +680,2 +681,0 +682,1 +683,1 +684,3 +685,1 +686,3 +687,0 +688,1 +689,3 +690,1 +691,0 +692,0 +693,0 +694,2 +695,3 +696,0 +697,0 +698,1 +699,0 +700,0 +701,0 +702,2 +703,0 +704,0 +705,1 +706,0 +707,2 +708,2 +709,0 +710,0 +711,1 +712,0 +713,0 +714,3 +715,1 +716,0 +717,0 +718,0 +719,2 +720,3 +721,1 +722,2 +723,2 +724,2 +725,0 +726,0 +727,0 +728,3 +729,0 +730,0 +731,3 +732,2 +733,2 +734,1 +735,2 +736,0 +737,0 +738,0 +739,3 +740,0 +741,0 +742,0 +743,1 +744,0 +745,0 +746,1 +747,3 +748,3 +749,2 +750,3 +751,3 +752,0 +753,0 +754,3 +755,3 +756,3 +757,2 +758,2 +759,1 +760,3 +761,3 +762,0 +763,1 +764,3 +765,3 +766,3 +767,0 +768,0 +769,3 +770,2 +771,0 +772,0 +773,1 +774,0 +775,0 +776,0 +777,0 +778,0 +779,0 +780,3 +781,0 +782,0 +783,1 +784,3 +785,0 +786,0 +787,2 +788,0 +789,0 +790,1 +791,3 +792,2 +793,1 +794,3 +795,2 +796,2 +797,1 +798,3 +799,0 +800,0 +801,2 +802,2 +803,2 +804,3 +805,0 +806,0 +807,2 +808,2 +809,2 +810,2 +811,1 +812,1 +813,0 +814,2 +815,2 +816,3 +817,3 +818,3 +819,1 +820,1 +821,2 +822,1 +823,0 +824,0 +825,0 +826,0 +827,3 +828,1 +829,1 +830,3 +831,2 +832,1 +833,0 +834,0 +835,0 +836,3 +837,0 +838,3 +839,3 +840,1 +841,0 +842,2 +843,0 +844,0 +845,0 +846,1 +847,0 +848,3 +849,0 +850,0 +851,0 +852,0 +853,2 +854,0 +855,2 +856,0 +857,3 +858,1 +859,0 +860,0 +861,1 +862,3 +863,1 +864,1 +865,0 +866,2 +867,1 +868,3 +869,1 +870,2 +871,3 +872,3 +873,0 +874,0 +875,1 +876,3 +877,0 +878,0 +879,1 +880,3 +881,0 +882,3 +883,1 +884,1 +885,2 +886,0 +887,0 +888,0 +889,0 +890,0 +891,2 +892,0 +893,0 +894,0 +895,0 +896,2 +897,2 +898,2 +899,0 +900,0 +901,1 +902,0 +903,3 +904,2 +905,2 +906,2 +907,2 +908,0 +909,3 +910,0 +911,2 +912,1 +913,1 +914,2 +915,0 +916,2 +917,0 +918,3 +919,0 +920,0 +921,2 +922,0 +923,3 +924,0 +925,1 +926,0 +927,0 +928,3 +929,0 +930,2 +931,3 +932,1 +933,0 +934,3 +935,0 +936,3 +937,2 +938,3 +939,2 +940,0 +941,3 +942,0 +943,0 +944,0 +945,3 +946,3 +947,0 +948,2 +949,0 +950,0 +951,0 +952,1 +953,1 +954,0 +955,0 +956,3 +957,3 +958,2 +959,0 +960,1 +961,3 +962,0 +963,3 +964,0 +965,0 +966,0 +967,0 +968,2 +969,2 +970,2 +971,0 +972,0 +973,2 +974,1 +975,0 +976,1 +977,2 +978,2 +979,2 +980,3 +981,3 +982,3 +983,0 +984,3 +985,1 +986,0 +987,0 +988,0 +989,2 +990,1 +991,0 +992,0 +993,0 +994,3 +995,2 +996,3 +997,2 +998,0 +999,1 +1000,0 +1001,0 +1002,0 +1003,2 +1004,0 +1005,2 +1006,2 +1007,0 +1008,2 +1009,2 +1010,3 +1011,3 +1012,3 +1013,0 +1014,1 +1015,0 +1016,2 +1017,1 +1018,3 +1019,2 +1020,3 +1021,3 +1022,0 +1023,0 +1024,0 +1025,1 +1026,3 +1027,3 +1028,0 +1029,0 +1030,0 +1031,0 +1032,1 +1033,2 +1034,0 +1035,0 +1036,3 +1037,0 +1038,1 +1039,1 +1040,0 +1041,2 +1042,3 +1043,1 +1044,3 +1045,3 +1046,3 +1047,1 +1048,1 +1049,1 +1050,1 +1051,2 +1052,2 +1053,0 +1054,1 +1055,0 +1056,3 +1057,2 +1058,0 +1059,0 +1060,0 +1061,0 +1062,0 +1063,1 +1064,0 +1065,3 +1066,1 +1067,2 +1068,3 +1069,0 +1070,0 +1071,0 +1072,1 +1073,0 +1074,2 +1075,2 +1076,0 +1077,2 +1078,3 +1079,0 +1080,0 +1081,0 +1082,0 +1083,2 +1084,1 +1085,2 +1086,2 +1087,3 +1088,0 +1089,2 +1090,1 +1091,0 +1092,3 +1093,0 +1094,2 +1095,0 +1096,0 +1097,0 +1098,1 +1099,0 +1100,3 +1101,0 +1102,3 +1103,1 +1104,1 +1105,0 +1106,2 +1107,3 +1108,2 +1109,3 +1110,1 +1111,3 +1112,0 +1113,0 +1114,0 +1115,0 +1116,0 +1117,3 +1118,0 +1119,3 +1120,1 +1121,0 +1122,0 +1123,2 +1124,2 +1125,0 +1126,0 +1127,1 +1128,1 +1129,0 +1130,0 +1131,1 +1132,0 +1133,2 +1134,3 +1135,0 +1136,0 +1137,3 +1138,3 +1139,1 +1140,3 +1141,0 +1142,0 +1143,0 +1144,3 +1145,0 +1146,3 +1147,0 +1148,0 +1149,1 +1150,0 +1151,2 +1152,0 +1153,3 +1154,0 +1155,3 +1156,0 +1157,3 +1158,0 +1159,0 +1160,1 +1161,3 +1162,1 +1163,0 +1164,2 +1165,2 +1166,1 +1167,3 +1168,2 +1169,3 +1170,0 +1171,0 +1172,0 +1173,1 +1174,2 +1175,2 +1176,0 +1177,0 +1178,0 +1179,1 +1180,1 +1181,2 +1182,1 +1183,0 +1184,0 +1185,3 +1186,2 +1187,3 +1188,0 +1189,1 +1190,2 +1191,0 +1192,0 +1193,0 +1194,0 +1195,0 +1196,2 +1197,0 +1198,0 +1199,3 +1200,3 +1201,0 +1202,0 +1203,0 +1204,0 +1205,2 +1206,0 +1207,2 +1208,2 +1209,1 +1210,2 +1211,2 +1212,0 +1213,3 +1214,1 +1215,0 +1216,0 +1217,0 +1218,0 +1219,0 +1220,3 +1221,3 +1222,0 +1223,3 +1224,2 +1225,0 +1226,2 +1227,0 +1228,3 +1229,1 +1230,1 +1231,1 +1232,1 +1233,2 +1234,1 +1235,2 +1236,3 +1237,0 +1238,2 +1239,0 +1240,0 +1241,3 +1242,1 +1243,1 +1244,3 +1245,3 +1246,0 +1247,3 +1248,0 +1249,0 +1250,3 +1251,0 +1252,0 +1253,1 +1254,0 +1255,3 +1256,0 +1257,1 +1258,0 +1259,0 +1260,1 +1261,0 +1262,1 +1263,2 +1264,2 +1265,0 +1266,0 +1267,3 +1268,2 +1269,2 +1270,0 +1271,3 +1272,0 +1273,2 +1274,0 +1275,1 +1276,0 +1277,0 +1278,0 +1279,3 +1280,3 +1281,0 +1282,3 +1283,0 +1284,2 +1285,3 +1286,3 +1287,0 +1288,0 +1289,3 +1290,0 +1291,0 +1292,0 +1293,3 +1294,2 +1295,3 +1296,0 +1297,0 +1298,2 +1299,0 +1300,2 +1301,2 +1302,2 +1303,0 +1304,3 +1305,3 +1306,2 +1307,0 +1308,0 +1309,0 +1310,1 +1311,3 +1312,3 +1313,1 +1314,2 +1315,2 +1316,0 +1317,0 +1318,1 +1319,3 +1320,1 +1321,3 +1322,0 +1323,0 +1324,2 +1325,0 +1326,0 +1327,2 +1328,0 +1329,0 +1330,0 +1331,0 +1332,0 +1333,3 +1334,3 +1335,2 +1336,0 +1337,3 +1338,0 +1339,3 +1340,0 +1341,0 +1342,0 +1343,0 +1344,0 +1345,0 +1346,1 +1347,0 +1348,0 +1349,2 +1350,1 +1351,0 +1352,0 +1353,1 +1354,2 +1355,0 +1356,2 +1357,3 +1358,2 +1359,0 +1360,3 +1361,3 +1362,0 +1363,0 +1364,0 +1365,0 +1366,3 +1367,2 +1368,0 +1369,0 +1370,2 +1371,0 +1372,0 +1373,0 +1374,2 +1375,2 +1376,0 +1377,1 +1378,0 +1379,0 +1380,2 +1381,0 +1382,0 +1383,0 +1384,3 +1385,2 +1386,3 +1387,1 +1388,1 +1389,0 +1390,0 +1391,3 +1392,1 +1393,2 +1394,3 +1395,0 +1396,3 +1397,0 +1398,0 +1399,0 +1400,0 +1401,0 +1402,0 +1403,2 +1404,0 +1405,3 +1406,3 +1407,2 +1408,0 +1409,0 +1410,0 +1411,1 +1412,0 +1413,0 +1414,0 +1415,0 +1416,1 +1417,0 +1418,0 +1419,0 +1420,0 +1421,0 +1422,2 +1423,0 +1424,1 +1425,3 +1426,3 +1427,0 +1428,0 +1429,0 +1430,0 +1431,0 +1432,3 +1433,1 +1434,3 +1435,0 +1436,0 +1437,3 +1438,1 +1439,2 +1440,1 +1441,2 +1442,0 +1443,0 +1444,0 +1445,3 +1446,0 +1447,0 +1448,2 +1449,2 +1450,3 +1451,0 +1452,3 +1453,3 +1454,3 +1455,1 +1456,0 +1457,0 +1458,0 +1459,3 +1460,0 +1461,0 +1462,2 +1463,3 +1464,3 +1465,1 +1466,0 +1467,3 +1468,0 +1469,0 +1470,0 +1471,1 +1472,0 +1473,0 +1474,3 +1475,0 +1476,1 +1477,0 +1478,3 +1479,3 +1480,0 +1481,1 +1482,3 +1483,1 +1484,3 +1485,0 +1486,1 +1487,0 +1488,3 +1489,0 +1490,0 +1491,0 +1492,1 +1493,3 +1494,0 +1495,1 +1496,2 +1497,3 +1498,0 +1499,0 +1500,0 +1501,0 +1502,3 +1503,0 +1504,0 +1505,1 +1506,2 +1507,0 +1508,2 +1509,3 +1510,2 +1511,3 +1512,0 +1513,3 +1514,3 +1515,2 +1516,3 +1517,3 +1518,2 +1519,3 +1520,2 +1521,3 +1522,0 +1523,0 +1524,1 +1525,0 +1526,1 +1527,1 +1528,1 +1529,0 +1530,3 +1531,1 +1532,3 +1533,3 +1534,3 +1535,2 +1536,0 +1537,1 +1538,2 +1539,0 +1540,0 +1541,0 +1542,1 +1543,0 +1544,0 +1545,1 +1546,1 +1547,1 +1548,2 +1549,2 +1550,2 +1551,0 +1552,1 +1553,0 +1554,0 +1555,3 +1556,0 +1557,2 +1558,1 +1559,0 +1560,0 +1561,2 +1562,0 +1563,0 +1564,2 +1565,3 +1566,0 +1567,3 +1568,0 +1569,0 +1570,3 +1571,3 +1572,3 +1573,0 +1574,2 +1575,1 +1576,3 +1577,1 +1578,1 +1579,0 +1580,1 +1581,2 +1582,0 +1583,1 +1584,0 +1585,0 +1586,0 +1587,0 +1588,0 +1589,0 +1590,0 +1591,3 +1592,2 +1593,2 +1594,0 +1595,0 +1596,0 +1597,3 +1598,3 +1599,0 +1600,1 +1601,1 +1602,0 +1603,0 +1604,3 +1605,0 +1606,2 +1607,0 +1608,1 +1609,1 +1610,2 +1611,3 +1612,0 +1613,0 +1614,0 +1615,0 +1616,2 +1617,3 +1618,3 +1619,0 +1620,2 +1621,3 +1622,3 +1623,1 +1624,0 +1625,3 +1626,2 +1627,0 +1628,0 +1629,3 +1630,3 +1631,0 +1632,2 +1633,0 +1634,0 +1635,1 +1636,2 +1637,3 +1638,1 +1639,0 +1640,0 +1641,0 +1642,3 +1643,1 +1644,0 +1645,0 +1646,2 +1647,3 +1648,0 +1649,2 +1650,3 +1651,1 +1652,1 +1653,3 +1654,1 +1655,2 +1656,0 +1657,1 +1658,0 +1659,1 +1660,0 +1661,3 +1662,2 +1663,0 +1664,1 +1665,2 +1666,0 +1667,3 +1668,2 +1669,0 +1670,3 +1671,2 +1672,0 +1673,0 +1674,2 +1675,0 +1676,0 +1677,3 +1678,1 +1679,1 +1680,3 +1681,2 +1682,3 +1683,0 +1684,2 +1685,2 +1686,0 +1687,2 +1688,0 +1689,0 +1690,1 +1691,3 +1692,0 +1693,3 +1694,1 +1695,3 +1696,3 +1697,0 +1698,0 +1699,0 +1700,0 +1701,0 +1702,1 +1703,0 +1704,1 +1705,0 +1706,1 +1707,0 +1708,2 +1709,0 +1710,3 +1711,2 +1712,0 +1713,2 +1714,0 +1715,1 +1716,0 +1717,0 +1718,3 +1719,0 +1720,1 +1721,3 +1722,2 +1723,3 +1724,0 +1725,1 +1726,1 +1727,1 +1728,0 +1729,0 +1730,2 +1731,0 +1732,1 +1733,1 +1734,3 +1735,0 +1736,3 +1737,0 +1738,2 +1739,0 +1740,0 +1741,2 +1742,3 +1743,1 +1744,2 +1745,1 +1746,3 +1747,1 +1748,0 +1749,0 +1750,0 +1751,0 +1752,2 +1753,0 +1754,0 +1755,2 +1756,0 +1757,3 +1758,3 +1759,1 +1760,0 +1761,0 +1762,0 +1763,3 +1764,0 +1765,0 +1766,3 +1767,1 +1768,0 +1769,1 +1770,3 +1771,3 +1772,2 +1773,3 +1774,1 +1775,2 +1776,1 +1777,3 +1778,0 +1779,0 +1780,1 +1781,3 +1782,0 +1783,0 +1784,0 +1785,0 +1786,0 +1787,2 +1788,0 +1789,1 +1790,2 +1791,0 +1792,2 +1793,0 +1794,2 +1795,0 +1796,3 +1797,0 +1798,1 +1799,0 +1800,2 +1801,2 +1802,0 +1803,0 +1804,3 +1805,0 +1806,3 +1807,1 +1808,3 +1809,0 +1810,1 +1811,0 +1812,3 +1813,2 +1814,0 +1815,1 +1816,3 +1817,0 +1818,0 +1819,2 +1820,3 +1821,0 +1822,0 +1823,0 +1824,0 +1825,0 +1826,2 +1827,1 +1828,0 +1829,2 +1830,0 +1831,3 +1832,2 +1833,2 +1834,2 +1835,0 +1836,2 +1837,0 +1838,0 +1839,0 +1840,0 +1841,0 +1842,0 +1843,0 +1844,3 +1845,2 +1846,1 +1847,1 +1848,0 +1849,1 +1850,2 +1851,3 +1852,3 +1853,0 +1854,0 +1855,2 +1856,0 +1857,0 +1858,3 +1859,1 +1860,0 +1861,1 +1862,2 +1863,0 +1864,1 +1865,0 +1866,0 +1867,1 +1868,0 +1869,0 +1870,0 +1871,2 +1872,2 +1873,1 +1874,0 +1875,3 +1876,0 +1877,3 +1878,0 +1879,2 +1880,0 +1881,1 +1882,2 +1883,0 +1884,3 +1885,0 +1886,2 +1887,0 +1888,0 +1889,1 +1890,0 +1891,2 +1892,2 +1893,0 +1894,0 +1895,1 +1896,0 +1897,0 +1898,2 +1899,0 +1900,0 +1901,3 +1902,2 +1903,2 +1904,2 +1905,0 +1906,0 +1907,1 +1908,0 +1909,3 +1910,0 +1911,2 +1912,0 +1913,1 +1914,0 +1915,2 +1916,0 +1917,0 +1918,0 +1919,0 +1920,1 +1921,3 +1922,0 +1923,3 +1924,3 +1925,1 +1926,0 +1927,0 +1928,0 +1929,2 +1930,2 +1931,0 +1932,0 +1933,0 +1934,1 +1935,1 +1936,0 +1937,3 +1938,1 +1939,3 +1940,1 +1941,0 +1942,2 +1943,2 +1944,0 +1945,0 +1946,1 +1947,1 +1948,1 +1949,0 +1950,0 +1951,0 +1952,0 +1953,0 +1954,0 +1955,0 +1956,1 +1957,2 +1958,3 +1959,3 +1960,0 +1961,1 +1962,2 +1963,2 +1964,3 +1965,0 +1966,2 +1967,2 +1968,3 +1969,0 +1970,1 +1971,0 +1972,0 +1973,0 +1974,3 +1975,2 +1976,0 +1977,3 +1978,1 +1979,0 +1980,2 +1981,1 +1982,0 +1983,0 +1984,1 +1985,0 +1986,2 +1987,0 +1988,3 +1989,0 +1990,0 +1991,3 +1992,0 +1993,2 +1994,0 +1995,0 +1996,3 +1997,2 +1998,2 +1999,0 +2000,0 +2001,0 +2002,0 +2003,0 +2004,1 +2005,0 +2006,0 +2007,2 +2008,0 +2009,3 +2010,0 +2011,1 +2012,1 +2013,0 +2014,1 +2015,0 +2016,3 +2017,0 +2018,0 +2019,2 +2020,2 +2021,2 +2022,0 +2023,3 +2024,1 +2025,0 +2026,0 +2027,0 +2028,0 +2029,3 +2030,2 +2031,0 +2032,2 +2033,0 +2034,3 +2035,0 +2036,0 +2037,2 +2038,0 +2039,0 +2040,2 +2041,0 +2042,0 +2043,0 +2044,2 +2045,0 +2046,3 +2047,0 +2048,0 +2049,2 +2050,0 +2051,0 +2052,3 +2053,0 +2054,0 +2055,2 +2056,0 +2057,1 +2058,1 +2059,0 +2060,0 +2061,3 +2062,2 +2063,0 +2064,0 +2065,0 +2066,2 +2067,0 +2068,0 +2069,0 +2070,2 +2071,3 +2072,0 +2073,1 +2074,0 +2075,0 +2076,0 +2077,3 +2078,0 +2079,1 +2080,0 +2081,1 +2082,3 +2083,0 +2084,0 +2085,3 +2086,3 +2087,0 +2088,0 +2089,3 +2090,1 +2091,3 +2092,2 +2093,2 +2094,2 +2095,1 +2096,0 +2097,3 +2098,0 +2099,1 +2100,0 +2101,0 +2102,1 +2103,1 +2104,2 +2105,0 +2106,0 +2107,2 +2108,0 +2109,2 +2110,2 +2111,0 +2112,1 +2113,0 +2114,0 +2115,1 +2116,2 +2117,1 +2118,0 +2119,2 +2120,2 +2121,0 +2122,0 +2123,0 +2124,0 +2125,0 +2126,0 +2127,0 +2128,0 +2129,3 +2130,3 +2131,1 +2132,0 +2133,1 +2134,2 +2135,0 +2136,1 +2137,0 +2138,0 +2139,2 +2140,3 +2141,3 +2142,0 +2143,0 +2144,0 +2145,3 +2146,0 +2147,0 +2148,3 +2149,3 +2150,2 +2151,1 +2152,1 +2153,0 +2154,2 +2155,0 +2156,2 +2157,2 +2158,2 +2159,1 +2160,0 +2161,2 +2162,0 +2163,3 +2164,3 +2165,3 +2166,1 +2167,3 +2168,0 +2169,1 +2170,3 +2171,3 +2172,2 +2173,0 +2174,0 +2175,0 +2176,3 +2177,2 +2178,0 +2179,0 +2180,0 +2181,0 +2182,3 +2183,1 +2184,0 +2185,1 +2186,0 +2187,1 +2188,3 +2189,1 +2190,0 +2191,1 +2192,3 +2193,3 +2194,1 +2195,1 +2196,3 +2197,3 +2198,2 +2199,2 +2200,1 +2201,0 +2202,1 +2203,3 +2204,0 +2205,3 +2206,1 +2207,0 +2208,0 +2209,3 +2210,0 +2211,3 +2212,0 +2213,3 +2214,1 +2215,0 +2216,2 +2217,0 +2218,0 +2219,2 +2220,0 +2221,3 +2222,2 +2223,0 +2224,0 +2225,2 +2226,0 +2227,0 +2228,1 +2229,0 +2230,2 +2231,0 +2232,0 +2233,3 +2234,0 +2235,0 +2236,0 +2237,0 +2238,2 +2239,0 +2240,1 +2241,1 +2242,0 +2243,0 +2244,0 +2245,2 +2246,0 +2247,2 +2248,3 +2249,0 +2250,2 +2251,0 +2252,0 +2253,0 +2254,0 +2255,3 +2256,1 +2257,0 +2258,3 +2259,0 +2260,1 +2261,3 +2262,3 +2263,2 +2264,1 +2265,2 +2266,2 +2267,2 +2268,0 +2269,2 +2270,3 +2271,0 +2272,3 +2273,3 +2274,1 +2275,0 +2276,3 +2277,0 +2278,0 +2279,2 +2280,3 +2281,0 +2282,0 +2283,1 +2284,0 +2285,2 +2286,3 +2287,2 +2288,3 +2289,2 +2290,0 +2291,0 +2292,0 +2293,3 +2294,3 +2295,0 +2296,2 +2297,0 +2298,3 +2299,0 +2300,0 +2301,3 +2302,0 +2303,0 +2304,0 +2305,3 +2306,1 +2307,0 +2308,0 +2309,1 +2310,2 +2311,0 +2312,0 +2313,1 +2314,2 +2315,2 +2316,0 +2317,2 +2318,3 +2319,0 +2320,2 +2321,0 +2322,0 +2323,0 +2324,3 +2325,1 +2326,3 +2327,0 +2328,0 +2329,3 +2330,2 +2331,0 +2332,1 +2333,0 +2334,3 +2335,1 +2336,0 +2337,3 +2338,0 +2339,1 +2340,0 +2341,3 +2342,0 +2343,0 +2344,0 +2345,1 +2346,1 +2347,3 +2348,0 +2349,0 +2350,0 +2351,0 +2352,0 +2353,2 +2354,3 +2355,3 +2356,3 +2357,1 +2358,0 +2359,0 +2360,2 +2361,0 +2362,3 +2363,2 +2364,2 +2365,3 +2366,3 +2367,3 +2368,0 +2369,0 +2370,3 +2371,1 +2372,0 +2373,0 +2374,1 +2375,1 +2376,3 +2377,0 +2378,1 +2379,1 +2380,2 +2381,2 +2382,2 +2383,0 +2384,1 +2385,3 +2386,0 +2387,1 +2388,3 +2389,3 +2390,0 +2391,3 +2392,0 +2393,2 +2394,3 +2395,3 +2396,3 +2397,0 +2398,3 +2399,3 +2400,0 +2401,3 +2402,0 +2403,0 +2404,1 +2405,3 +2406,3 +2407,0 +2408,1 +2409,0 +2410,3 +2411,2 +2412,1 +2413,3 +2414,0 +2415,0 +2416,3 +2417,0 +2418,2 +2419,0 +2420,0 +2421,1 +2422,3 +2423,2 +2424,1 +2425,0 +2426,3 +2427,2 +2428,0 +2429,2 +2430,3 +2431,1 +2432,2 +2433,0 +2434,2 +2435,1 +2436,3 +2437,3 +2438,2 +2439,0 +2440,0 +2441,0 +2442,2 +2443,3 +2444,1 +2445,0 +2446,3 +2447,2 +2448,0 +2449,0 +2450,3 +2451,3 +2452,0 +2453,2 +2454,3 +2455,0 +2456,0 +2457,0 +2458,1 +2459,2 +2460,0 +2461,0 +2462,2 +2463,3 +2464,0 +2465,0 +2466,2 +2467,3 +2468,3 +2469,3 +2470,0 +2471,0 +2472,0 +2473,2 +2474,3 +2475,0 +2476,0 +2477,0 +2478,3 +2479,3 +2480,1 +2481,0 +2482,3 +2483,0 +2484,3 +2485,1 +2486,3 +2487,0 +2488,3 +2489,1 +2490,2 +2491,0 +2492,2 +2493,1 +2494,0 +2495,0 +2496,1 +2497,2 +2498,1 +2499,0 +2500,2 +2501,3 +2502,3 +2503,1 +2504,2 +2505,0 +2506,0 +2507,0 +2508,2 +2509,3 +2510,0 +2511,0 +2512,0 +2513,3 +2514,1 +2515,0 +2516,3 +2517,0 +2518,1 +2519,1 +2520,0 +2521,0 +2522,2 +2523,1 +2524,0 +2525,0 +2526,3 +2527,0 +2528,0 +2529,0 +2530,0 +2531,0 +2532,2 +2533,0 +2534,0 +2535,1 +2536,2 +2537,0 +2538,0 +2539,3 +2540,0 +2541,2 +2542,3 +2543,2 +2544,2 +2545,1 +2546,3 +2547,0 +2548,1 +2549,0 +2550,3 +2551,3 +2552,2 +2553,0 +2554,2 +2555,0 +2556,0 +2557,2 +2558,0 +2559,0 +2560,0 +2561,3 +2562,2 +2563,2 +2564,0 +2565,3 +2566,2 +2567,3 +2568,0 +2569,2 +2570,2 +2571,1 +2572,0 +2573,2 +2574,0 +2575,1 +2576,1 +2577,0 +2578,2 +2579,0 +2580,1 +2581,1 +2582,3 +2583,3 +2584,3 +2585,1 +2586,0 +2587,2 +2588,3 +2589,3 +2590,2 +2591,2 +2592,2 +2593,0 +2594,1 +2595,0 +2596,1 +2597,0 +2598,1 +2599,0 +2600,3 +2601,3 +2602,3 +2603,0 +2604,2 +2605,1 +2606,2 +2607,0 +2608,2 +2609,3 +2610,0 +2611,3 +2612,0 +2613,0 +2614,0 +2615,0 +2616,0 +2617,3 +2618,0 +2619,2 +2620,1 +2621,3 +2622,2 +2623,1 +2624,1 +2625,2 +2626,0 +2627,0 +2628,1 +2629,3 +2630,0 +2631,0 +2632,0 +2633,1 +2634,2 +2635,1 +2636,0 +2637,3 +2638,3 +2639,1 +2640,3 +2641,0 +2642,2 +2643,2 +2644,3 +2645,0 +2646,0 +2647,0 +2648,1 +2649,0 +2650,2 +2651,0 +2652,0 +2653,1 +2654,0 +2655,0 +2656,2 +2657,2 +2658,0 +2659,0 +2660,2 +2661,1 +2662,0 +2663,3 +2664,2 +2665,3 +2666,0 +2667,2 +2668,2 +2669,0 +2670,3 +2671,1 +2672,3 +2673,0 +2674,0 +2675,0 +2676,0 +2677,1 +2678,3 +2679,0 +2680,3 +2681,2 +2682,2 +2683,0 +2684,3 +2685,1 +2686,0 +2687,2 +2688,0 +2689,0 +2690,1 +2691,0 +2692,2 +2693,2 +2694,2 +2695,1 +2696,3 +2697,2 +2698,0 +2699,3 +2700,2 +2701,3 +2702,0 +2703,1 +2704,0 +2705,2 +2706,3 +2707,1 +2708,0 +2709,2 +2710,1 +2711,0 +2712,0 +2713,2 +2714,0 +2715,3 +2716,1 +2717,0 +2718,1 +2719,3 +2720,3 +2721,0 +2722,0 +2723,0 +2724,3 +2725,1 +2726,0 +2727,0 +2728,3 +2729,2 +2730,0 +2731,0 +2732,3 +2733,2 +2734,0 +2735,0 +2736,2 +2737,1 +2738,0 +2739,0 +2740,0 +2741,0 +2742,0 +2743,1 +2744,1 +2745,0 +2746,3 +2747,1 +2748,3 +2749,0 +2750,3 +2751,3 +2752,0 +2753,0 +2754,2 +2755,2 +2756,0 +2757,2 +2758,2 +2759,1 +2760,3 +2761,0 +2762,0 +2763,0 +2764,0 +2765,0 +2766,1 +2767,3 +2768,0 +2769,0 +2770,3 +2771,0 +2772,3 +2773,0 +2774,0 +2775,0 +2776,0 +2777,1 +2778,3 +2779,3 +2780,0 +2781,2 +2782,0 +2783,2 +2784,2 +2785,0 +2786,2 +2787,0 +2788,3 +2789,0 +2790,0 +2791,0 +2792,0 +2793,1 +2794,0 +2795,0 +2796,2 +2797,0 +2798,2 +2799,1 +2800,3 +2801,0 +2802,0 +2803,0 +2804,3 +2805,3 +2806,0 +2807,3 +2808,3 +2809,0 +2810,1 +2811,0 +2812,0 +2813,1 +2814,0 +2815,3 +2816,0 +2817,0 +2818,2 +2819,3 +2820,2 +2821,2 +2822,2 +2823,2 +2824,0 +2825,0 +2826,0 +2827,0 +2828,3 +2829,2 +2830,0 +2831,0 +2832,3 +2833,0 +2834,2 +2835,0 +2836,0 +2837,3 +2838,2 +2839,2 +2840,2 +2841,3 +2842,1 +2843,1 +2844,0 +2845,2 +2846,2 +2847,3 +2848,0 +2849,0 +2850,3 +2851,0 +2852,0 +2853,3 +2854,3 +2855,0 +2856,0 +2857,0 +2858,1 +2859,0 +2860,2 +2861,0 +2862,0 +2863,0 +2864,1 +2865,1 +2866,0 +2867,3 +2868,0 +2869,3 +2870,3 +2871,1 +2872,0 +2873,2 +2874,2 +2875,0 +2876,0 +2877,3 +2878,2 +2879,3 +2880,0 +2881,3 +2882,3 +2883,2 +2884,3 +2885,3 +2886,3 +2887,1 +2888,1 +2889,1 +2890,0 +2891,3 +2892,0 +2893,3 +2894,0 +2895,3 +2896,3 +2897,1 +2898,3 +2899,0 +2900,3 +2901,3 +2902,3 +2903,2 +2904,3 +2905,2 +2906,2 +2907,0 +2908,3 +2909,3 +2910,1 +2911,1 +2912,0 +2913,2 +2914,3 +2915,2 +2916,3 +2917,3 +2918,3 +2919,0 +2920,0 +2921,0 +2922,3 +2923,1 +2924,0 +2925,0 +2926,0 +2927,0 +2928,2 +2929,3 +2930,2 +2931,1 +2932,0 +2933,2 +2934,0 +2935,0 +2936,0 +2937,2 +2938,0 +2939,0 +2940,2 +2941,3 +2942,0 +2943,0 +2944,1 +2945,0 +2946,0 +2947,0 +2948,2 +2949,0 +2950,0 +2951,2 +2952,0 +2953,2 +2954,1 +2955,0 +2956,2 +2957,3 +2958,0 +2959,0 +2960,0 +2961,0 +2962,3 +2963,3 +2964,0 +2965,2 +2966,2 +2967,1 +2968,1 +2969,0 +2970,0 +2971,1 +2972,0 +2973,0 +2974,2 +2975,2 +2976,3 +2977,3 +2978,1 +2979,0 +2980,2 +2981,0 +2982,3 +2983,1 +2984,2 +2985,0 +2986,2 +2987,3 +2988,0 +2989,2 +2990,0 +2991,0 +2992,0 +2993,0 +2994,0 +2995,3 +2996,0 +2997,0 +2998,3 +2999,0 +3000,0 +3001,0 +3002,3 +3003,2 +3004,0 +3005,3 +3006,1 +3007,0 +3008,3 +3009,1 +3010,2 +3011,1 +3012,0 +3013,3 +3014,1 +3015,3 +3016,0 +3017,0 +3018,0 +3019,3 +3020,2 +3021,0 +3022,1 +3023,3 +3024,0 +3025,3 +3026,0 +3027,1 +3028,0 +3029,3 +3030,3 +3031,0 +3032,3 +3033,2 +3034,0 +3035,3 +3036,3 +3037,0 +3038,0 +3039,0 +3040,3 +3041,1 +3042,0 +3043,0 +3044,1 +3045,0 +3046,0 +3047,0 +3048,0 +3049,0 +3050,0 +3051,0 +3052,0 +3053,1 +3054,0 +3055,2 +3056,0 +3057,2 +3058,0 +3059,3 +3060,0 +3061,0 +3062,3 +3063,0 +3064,3 +3065,3 +3066,0 +3067,3 +3068,1 +3069,0 +3070,0 +3071,3 +3072,3 +3073,3 +3074,3 +3075,0 +3076,0 +3077,0 +3078,0 +3079,0 +3080,3 +3081,3 +3082,0 +3083,0 +3084,1 +3085,0 +3086,3 +3087,0 +3088,3 +3089,2 +3090,0 +3091,2 +3092,1 +3093,0 +3094,0 +3095,1 +3096,0 +3097,3 +3098,3 +3099,2 +3100,2 +3101,0 +3102,0 +3103,0 +3104,0 +3105,1 +3106,0 +3107,0 +3108,0 +3109,0 +3110,3 +3111,0 +3112,2 +3113,1 +3114,0 +3115,0 +3116,0 +3117,0 +3118,0 +3119,0 +3120,1 +3121,2 +3122,0 +3123,0 +3124,0 +3125,2 +3126,0 +3127,2 +3128,2 +3129,0 +3130,0 +3131,3 +3132,1 +3133,0 +3134,0 +3135,3 +3136,3 +3137,3 +3138,1 +3139,0 +3140,0 +3141,1 +3142,2 +3143,0 +3144,3 +3145,0 +3146,3 +3147,0 +3148,0 +3149,0 +3150,1 +3151,0 +3152,0 +3153,0 +3154,0 +3155,3 +3156,3 +3157,3 +3158,1 +3159,2 +3160,2 +3161,2 +3162,1 +3163,0 +3164,3 +3165,3 +3166,3 +3167,1 +3168,0 +3169,0 +3170,2 +3171,1 +3172,3 +3173,3 +3174,2 +3175,0 +3176,0 +3177,1 +3178,3 +3179,1 +3180,2 +3181,1 +3182,2 +3183,2 +3184,0 +3185,3 +3186,3 +3187,1 +3188,0 +3189,0 +3190,0 +3191,0 +3192,0 +3193,3 +3194,1 +3195,2 +3196,0 +3197,0 +3198,0 +3199,2 +3200,0 +3201,1 +3202,3 +3203,0 +3204,2 +3205,0 +3206,3 +3207,0 +3208,1 +3209,3 +3210,0 +3211,2 +3212,2 +3213,3 +3214,3 +3215,0 +3216,0 +3217,2 +3218,0 +3219,0 +3220,1 +3221,0 +3222,3 +3223,2 +3224,0 +3225,0 +3226,0 +3227,3 +3228,0 +3229,2 +3230,0 +3231,3 +3232,0 +3233,1 +3234,3 +3235,0 +3236,2 +3237,3 +3238,1 +3239,3 +3240,2 +3241,2 +3242,3 +3243,0 +3244,0 +3245,1 +3246,2 +3247,0 +3248,1 +3249,3 +3250,3 +3251,0 +3252,0 +3253,3 +3254,0 +3255,3 +3256,2 +3257,3 +3258,2 +3259,0 +3260,3 +3261,1 +3262,0 +3263,3 +3264,0 +3265,0 +3266,2 +3267,3 +3268,3 +3269,1 +3270,0 +3271,1 +3272,0 +3273,0 +3274,0 +3275,0 +3276,0 +3277,0 +3278,0 +3279,0 +3280,2 +3281,1 +3282,3 +3283,0 +3284,0 +3285,1 +3286,3 +3287,0 +3288,3 +3289,0 +3290,0 +3291,0 +3292,1 +3293,3 +3294,1 +3295,1 +3296,0 +3297,0 +3298,1 +3299,0 +3300,1 +3301,0 +3302,0 +3303,0 +3304,0 +3305,0 +3306,0 +3307,0 +3308,0 +3309,3 +3310,0 +3311,0 +3312,1 +3313,1 +3314,2 +3315,2 +3316,0 +3317,0 +3318,3 +3319,1 +3320,2 +3321,0 +3322,3 +3323,0 +3324,2 +3325,0 +3326,0 +3327,0 +3328,3 +3329,2 +3330,2 +3331,0 +3332,3 +3333,0 +3334,2 +3335,3 +3336,0 +3337,0 +3338,1 +3339,2 +3340,3 +3341,0 +3342,0 +3343,0 +3344,2 +3345,1 +3346,2 +3347,0 +3348,0 +3349,2 +3350,2 +3351,0 +3352,0 +3353,3 +3354,0 +3355,1 +3356,3 +3357,0 +3358,0 +3359,3 +3360,0 +3361,0 +3362,0 +3363,3 +3364,0 +3365,0 +3366,2 +3367,1 +3368,3 +3369,1 +3370,1 +3371,0 +3372,3 +3373,3 +3374,2 +3375,3 +3376,0 +3377,1 +3378,3 +3379,1 +3380,0 +3381,0 +3382,3 +3383,2 +3384,0 +3385,3 +3386,0 +3387,2 +3388,1 +3389,0 +3390,0 +3391,0 +3392,3 +3393,0 +3394,2 +3395,0 +3396,0 +3397,0 +3398,3 +3399,3 +3400,3 +3401,2 +3402,2 +3403,1 +3404,0 +3405,3 +3406,2 +3407,0 +3408,0 +3409,0 +3410,2 +3411,1 +3412,3 +3413,0 +3414,1 +3415,0 +3416,3 +3417,0 +3418,0 +3419,3 +3420,3 +3421,3 +3422,0 +3423,0 +3424,1 +3425,0 +3426,0 +3427,3 +3428,2 +3429,2 +3430,0 +3431,0 +3432,0 +3433,0 +3434,3 +3435,2 +3436,1 +3437,1 +3438,0 +3439,0 +3440,3 +3441,0 +3442,0 +3443,2 +3444,0 +3445,0 +3446,3 +3447,1 +3448,1 +3449,3 +3450,0 +3451,3 +3452,3 +3453,0 +3454,0 +3455,3 +3456,2 +3457,0 +3458,3 +3459,0 +3460,1 +3461,0 +3462,0 +3463,1 +3464,0 +3465,0 +3466,1 +3467,0 +3468,2 +3469,3 +3470,3 +3471,2 +3472,3 +3473,0 +3474,2 +3475,0 +3476,0 +3477,3 +3478,2 +3479,0 +3480,2 +3481,3 +3482,3 +3483,0 +3484,2 +3485,1 +3486,1 +3487,0 +3488,0 +3489,0 +3490,0 +3491,2 +3492,0 +3493,3 +3494,1 +3495,0 +3496,0 +3497,0 +3498,2 +3499,1 +3500,0 +3501,1 +3502,2 +3503,0 +3504,2 +3505,0 +3506,0 +3507,2 +3508,2 +3509,0 +3510,3 +3511,2 +3512,2 +3513,2 +3514,0 +3515,0 +3516,0 +3517,0 +3518,0 +3519,3 +3520,2 +3521,2 +3522,3 +3523,0 +3524,2 +3525,2 +3526,0 +3527,0 +3528,1 +3529,0 +3530,2 +3531,3 +3532,0 +3533,0 +3534,2 +3535,1 +3536,2 +3537,3 +3538,0 +3539,3 +3540,2 +3541,0 +3542,0 +3543,2 +3544,0 +3545,1 +3546,1 +3547,2 +3548,1 +3549,0 +3550,2 +3551,3 +3552,2 +3553,3 +3554,0 +3555,2 +3556,0 +3557,2 +3558,3 +3559,0 +3560,2 +3561,0 +3562,2 +3563,2 +3564,3 +3565,0 +3566,0 +3567,1 +3568,1 +3569,3 +3570,2 +3571,2 +3572,0 +3573,0 +3574,0 +3575,0 +3576,0 +3577,0 +3578,3 +3579,0 +3580,1 +3581,2 +3582,0 +3583,0 +3584,2 +3585,3 +3586,1 +3587,3 +3588,0 +3589,3 +3590,3 +3591,0 +3592,2 +3593,3 +3594,2 +3595,0 +3596,0 +3597,0 +3598,0 +3599,0 +3600,3 +3601,3 +3602,3 +3603,2 +3604,0 +3605,0 +3606,2 +3607,0 +3608,0 +3609,0 +3610,3 +3611,0 +3612,3 +3613,2 +3614,1 +3615,1 +3616,0 +3617,0 +3618,0 +3619,2 +3620,2 +3621,1 +3622,3 +3623,1 +3624,3 +3625,3 +3626,3 +3627,3 +3628,3 +3629,0 +3630,0 +3631,3 +3632,0 +3633,1 +3634,0 +3635,0 +3636,3 +3637,2 +3638,3 +3639,0 +3640,2 +3641,3 +3642,0 +3643,0 +3644,2 +3645,2 +3646,0 +3647,0 +3648,3 +3649,3 +3650,2 +3651,0 +3652,2 +3653,0 +3654,0 +3655,0 +3656,0 +3657,3 +3658,0 +3659,2 +3660,1 +3661,0 +3662,1 +3663,0 +3664,0 +3665,3 +3666,1 +3667,0 +3668,2 +3669,2 +3670,2 +3671,1 +3672,0 +3673,0 +3674,1 +3675,2 +3676,0 +3677,1 +3678,2 +3679,0 +3680,0 +3681,1 +3682,0 +3683,3 +3684,0 +3685,3 +3686,0 +3687,0 +3688,2 +3689,2 +3690,1 +3691,0 +3692,1 +3693,0 +3694,2 +3695,1 +3696,0 +3697,0 +3698,3 +3699,2 +3700,3 +3701,1 +3702,1 +3703,1 +3704,3 +3705,0 +3706,0 +3707,0 +3708,2 +3709,0 +3710,0 +3711,3 +3712,0 +3713,0 +3714,2 +3715,1 +3716,3 +3717,1 +3718,3 +3719,1 +3720,0 +3721,1 +3722,1 +3723,1 +3724,0 +3725,0 +3726,0 +3727,0 +3728,0 +3729,0 +3730,3 +3731,0 +3732,3 +3733,0 +3734,0 +3735,3 +3736,0 +3737,3 +3738,0 +3739,3 +3740,2 +3741,0 +3742,3 +3743,0 +3744,3 +3745,0 +3746,0 +3747,2 +3748,2 +3749,1 +3750,0 +3751,0 +3752,3 +3753,2 +3754,0 +3755,2 +3756,3 +3757,0 +3758,3 +3759,2 +3760,0 +3761,0 +3762,0 +3763,0 +3764,0 +3765,3 +3766,3 +3767,0 +3768,2 +3769,0 +3770,3 +3771,0 +3772,0 +3773,0 +3774,1 +3775,2 +3776,0 +3777,0 +3778,0 +3779,1 +3780,1 +3781,0 +3782,0 +3783,1 +3784,2 +3785,3 +3786,0 +3787,0 +3788,2 +3789,0 +3790,2 +3791,0 +3792,2 +3793,2 +3794,0 +3795,0 +3796,0 +3797,0 +3798,1 +3799,0 +3800,0 +3801,1 +3802,3 +3803,2 +3804,0 +3805,1 +3806,2 +3807,0 +3808,3 +3809,0 +3810,2 +3811,3 +3812,2 +3813,1 +3814,2 +3815,0 +3816,0 +3817,0 +3818,3 +3819,0 +3820,2 +3821,2 +3822,0 +3823,3 +3824,0 +3825,0 +3826,2 +3827,0 +3828,0 +3829,1 +3830,0 +3831,2 +3832,3 +3833,3 +3834,0 +3835,0 +3836,0 +3837,0 +3838,0 +3839,3 +3840,2 +3841,3 +3842,3 +3843,3 +3844,0 +3845,2 +3846,0 +3847,0 +3848,0 +3849,1 +3850,0 +3851,0 +3852,0 +3853,0 +3854,3 +3855,0 +3856,2 +3857,0 +3858,0 +3859,3 +3860,2 +3861,0 +3862,0 +3863,0 +3864,1 +3865,0 +3866,3 +3867,0 +3868,0 +3869,3 +3870,0 +3871,2 +3872,1 +3873,2 +3874,3 +3875,0 +3876,0 +3877,0 +3878,0 +3879,0 +3880,0 +3881,3 +3882,1 +3883,1 +3884,3 +3885,0 +3886,2 +3887,1 +3888,0 +3889,0 +3890,2 +3891,2 +3892,0 +3893,0 +3894,0 +3895,0 +3896,0 +3897,3 +3898,2 +3899,0 +3900,2 +3901,1 +3902,0 +3903,3 +3904,0 +3905,3 +3906,0 +3907,0 +3908,0 +3909,3 +3910,2 +3911,1 +3912,3 +3913,0 +3914,0 +3915,2 +3916,0 +3917,0 +3918,3 +3919,3 +3920,0 +3921,2 +3922,1 +3923,1 +3924,3 +3925,0 +3926,1 +3927,2 +3928,2 +3929,0 +3930,0 +3931,3 +3932,2 +3933,0 +3934,0 +3935,2 +3936,0 +3937,1 +3938,0 +3939,1 +3940,0 +3941,1 +3942,1 +3943,0 +3944,1 +3945,2 +3946,2 +3947,0 +3948,1 +3949,3 +3950,2 +3951,2 +3952,0 +3953,0 +3954,0 +3955,1 +3956,0 +3957,3 +3958,1 +3959,0 +3960,0 +3961,0 +3962,3 +3963,2 +3964,2 +3965,3 +3966,0 +3967,2 +3968,0 +3969,1 +3970,3 +3971,0 +3972,0 +3973,0 +3974,3 +3975,0 +3976,0 +3977,3 +3978,3 +3979,1 +3980,3 +3981,0 +3982,2 +3983,2 +3984,1 +3985,1 +3986,3 +3987,1 +3988,2 +3989,3 +3990,0 +3991,2 +3992,2 +3993,0 +3994,0 +3995,0 +3996,0 +3997,0 +3998,2 +3999,0 +4000,0 +4001,3 +4002,1 +4003,2 +4004,0 +4005,1 +4006,3 +4007,1 +4008,1 +4009,1 +4010,2 +4011,2 +4012,3 +4013,3 +4014,3 +4015,3 +4016,2 +4017,2 +4018,3 +4019,0 +4020,0 +4021,0 +4022,0 +4023,0 +4024,3 +4025,2 +4026,2 +4027,0 +4028,0 +4029,0 +4030,0 +4031,1 +4032,0 +4033,2 +4034,0 +4035,3 +4036,1 +4037,2 +4038,2 +4039,3 +4040,0 +4041,0 +4042,0 +4043,0 +4044,2 +4045,2 +4046,2 +4047,3 +4048,0 +4049,3 +4050,2 +4051,3 +4052,2 +4053,2 +4054,3 +4055,0 +4056,0 +4057,0 +4058,0 +4059,0 +4060,3 +4061,0 +4062,0 +4063,0 +4064,0 +4065,3 +4066,0 +4067,0 +4068,1 +4069,3 +4070,0 +4071,2 +4072,3 +4073,0 +4074,1 +4075,0 +4076,0 +4077,0 +4078,0 +4079,2 +4080,0 +4081,1 +4082,0 +4083,0 +4084,0 +4085,3 +4086,2 +4087,3 +4088,0 +4089,0 +4090,1 +4091,0 +4092,0 +4093,0 +4094,2 +4095,1 +4096,0 +4097,0 +4098,0 +4099,3 +4100,1 +4101,0 +4102,2 +4103,2 +4104,2 +4105,3 +4106,0 +4107,2 +4108,0 +4109,0 +4110,0 +4111,3 +4112,2 +4113,0 +4114,0 +4115,3 +4116,3 +4117,3 +4118,0 +4119,1 +4120,2 +4121,0 +4122,0 +4123,2 +4124,0 +4125,2 +4126,2 +4127,0 +4128,0 +4129,3 +4130,0 +4131,3 +4132,2 +4133,0 +4134,0 +4135,2 +4136,0 +4137,2 +4138,2 +4139,1 +4140,1 +4141,3 +4142,3 +4143,2 +4144,3 +4145,0 +4146,2 +4147,0 +4148,0 +4149,3 +4150,0 +4151,0 +4152,0 +4153,2 +4154,1 +4155,0 +4156,0 +4157,3 +4158,1 +4159,2 +4160,3 +4161,0 +4162,2 +4163,2 +4164,0 +4165,3 +4166,0 +4167,3 +4168,2 +4169,0 +4170,2 +4171,0 +4172,2 +4173,0 +4174,0 +4175,3 +4176,1 +4177,0 +4178,2 +4179,2 +4180,0 +4181,1 +4182,2 +4183,2 +4184,0 +4185,0 +4186,2 +4187,3 +4188,0 +4189,2 +4190,2 +4191,3 +4192,0 +4193,3 +4194,2 +4195,2 +4196,0 +4197,0 +4198,1 +4199,2 +4200,3 +4201,0 +4202,0 +4203,2 +4204,0 +4205,0 +4206,3 +4207,0 +4208,0 +4209,0 +4210,0 +4211,0 +4212,0 +4213,0 +4214,1 +4215,0 +4216,1 +4217,0 +4218,0 +4219,3 +4220,0 +4221,3 +4222,2 +4223,0 +4224,0 +4225,0 +4226,0 +4227,1 +4228,3 +4229,0 +4230,0 +4231,0 +4232,2 +4233,2 +4234,0 +4235,2 +4236,1 +4237,0 +4238,3 +4239,0 +4240,2 +4241,3 +4242,0 +4243,0 +4244,0 +4245,0 +4246,0 +4247,1 +4248,3 +4249,0 +4250,3 +4251,0 +4252,0 +4253,2 +4254,0 +4255,0 +4256,3 +4257,0 +4258,1 +4259,3 +4260,3 +4261,0 +4262,3 +4263,1 +4264,2 +4265,0 +4266,2 +4267,2 +4268,0 +4269,0 +4270,2 +4271,1 +4272,3 +4273,0 +4274,1 +4275,3 +4276,0 +4277,2 +4278,1 +4279,0 +4280,0 +4281,0 +4282,2 +4283,2 +4284,0 +4285,0 +4286,1 +4287,0 +4288,1 +4289,2 +4290,3 +4291,0 +4292,2 +4293,0 +4294,0 +4295,1 +4296,1 +4297,1 +4298,2 +4299,1 +4300,0 +4301,3 +4302,2 +4303,0 +4304,0 +4305,1 +4306,3 +4307,3 +4308,3 +4309,2 +4310,1 +4311,2 +4312,0 +4313,3 +4314,2 +4315,0 +4316,2 +4317,3 +4318,0 +4319,0 +4320,3 +4321,3 +4322,1 +4323,0 +4324,0 +4325,3 +4326,1 +4327,3 +4328,1 +4329,1 +4330,1 +4331,1 +4332,0 +4333,3 +4334,2 +4335,2 +4336,0 +4337,2 +4338,0 +4339,3 +4340,2 +4341,3 +4342,3 +4343,3 +4344,3 +4345,2 +4346,0 +4347,2 +4348,3 +4349,2 +4350,0 +4351,2 +4352,3 +4353,1 +4354,0 +4355,0 +4356,0 +4357,0 +4358,1 +4359,0 +4360,2 +4361,0 +4362,0 +4363,0 +4364,2 +4365,2 +4366,2 +4367,0 +4368,2 +4369,2 +4370,0 +4371,0 +4372,0 +4373,3 +4374,0 +4375,0 +4376,1 +4377,0 +4378,3 +4379,0 +4380,1 +4381,3 +4382,0 +4383,3 +4384,0 +4385,3 +4386,1 +4387,0 +4388,0 +4389,0 +4390,2 +4391,2 +4392,0 +4393,0 +4394,0 +4395,1 +4396,1 +4397,0 +4398,2 +4399,0 +4400,2 +4401,3 +4402,2 +4403,0 +4404,1 +4405,1 +4406,2 +4407,0 +4408,0 +4409,0 +4410,2 +4411,1 +4412,0 +4413,0 +4414,3 +4415,0 +4416,0 +4417,3 +4418,3 +4419,2 +4420,1 +4421,2 +4422,1 +4423,0 +4424,3 +4425,0 +4426,0 +4427,2 +4428,0 +4429,0 +4430,3 +4431,2 +4432,1 +4433,2 +4434,1 +4435,2 +4436,0 +4437,0 +4438,0 +4439,0 +4440,0 +4441,2 +4442,3 +4443,2 +4444,3 +4445,0 +4446,1 +4447,1 +4448,2 +4449,0 +4450,0 +4451,0 +4452,1 +4453,3 +4454,1 +4455,0 +4456,2 +4457,0 +4458,1 +4459,3 +4460,2 +4461,2 +4462,0 +4463,3 +4464,3 +4465,0 +4466,0 +4467,2 +4468,0 +4469,1 +4470,0 +4471,0 +4472,1 +4473,1 +4474,0 +4475,1 +4476,0 +4477,2 +4478,0 +4479,0 +4480,0 +4481,3 +4482,1 +4483,3 +4484,3 +4485,3 +4486,0 +4487,0 +4488,1 +4489,3 +4490,3 +4491,2 +4492,3 +4493,0 +4494,1 +4495,0 +4496,3 +4497,0 +4498,3 +4499,0 +4500,0 +4501,0 +4502,1 +4503,0 +4504,2 +4505,3 +4506,0 +4507,3 +4508,1 +4509,0 +4510,3 +4511,0 +4512,0 +4513,3 +4514,3 +4515,3 +4516,2 +4517,2 +4518,1 +4519,0 +4520,0 +4521,2 +4522,2 +4523,0 +4524,2 +4525,0 +4526,3 +4527,0 +4528,1 +4529,1 +4530,2 +4531,2 +4532,1 +4533,0 +4534,2 +4535,0 +4536,3 +4537,3 +4538,2 +4539,2 +4540,1 +4541,2 +4542,2 +4543,2 +4544,3 +4545,3 +4546,3 +4547,3 +4548,2 +4549,0 +4550,2 +4551,1 +4552,3 +4553,0 +4554,2 +4555,2 +4556,3 +4557,0 +4558,3 +4559,3 +4560,2 +4561,1 +4562,3 +4563,0 +4564,0 +4565,0 +4566,0 +4567,1 +4568,3 +4569,3 +4570,0 +4571,2 +4572,0 +4573,0 +4574,2 +4575,3 +4576,3 +4577,1 +4578,0 +4579,3 +4580,2 +4581,0 +4582,0 +4583,2 +4584,2 +4585,1 +4586,0 +4587,2 +4588,2 +4589,0 +4590,2 +4591,0 +4592,0 +4593,0 +4594,2 +4595,1 +4596,0 +4597,0 +4598,2 +4599,0 +4600,3 +4601,3 +4602,3 +4603,3 +4604,0 +4605,2 +4606,0 +4607,1 +4608,0 +4609,2 +4610,0 +4611,0 +4612,0 +4613,0 +4614,0 +4615,3 +4616,3 +4617,2 +4618,0 +4619,2 +4620,0 +4621,2 +4622,3 +4623,0 +4624,3 +4625,0 +4626,3 +4627,0 +4628,0 +4629,3 +4630,0 +4631,0 +4632,0 +4633,3 +4634,0 +4635,0 +4636,1 +4637,2 +4638,0 +4639,0 +4640,0 +4641,0 +4642,1 +4643,0 +4644,0 +4645,1 +4646,0 +4647,0 +4648,0 +4649,0 +4650,3 +4651,0 +4652,0 +4653,3 +4654,0 +4655,0 +4656,3 +4657,2 +4658,0 +4659,3 +4660,1 +4661,1 +4662,1 +4663,0 +4664,1 +4665,1 +4666,2 +4667,2 +4668,1 +4669,3 +4670,0 +4671,0 +4672,3 +4673,2 +4674,0 +4675,0 +4676,3 +4677,1 +4678,0 +4679,1 +4680,2 +4681,0 +4682,3 +4683,0 +4684,0 +4685,2 +4686,0 +4687,1 +4688,0 +4689,0 +4690,2 +4691,0 +4692,0 +4693,3 +4694,1 +4695,1 +4696,2 +4697,1 +4698,0 +4699,0 +4700,0 +4701,0 +4702,2 +4703,0 +4704,1 +4705,0 +4706,3 +4707,3 +4708,2 +4709,1 +4710,0 +4711,0 +4712,3 +4713,3 +4714,0 +4715,0 +4716,0 +4717,0 +4718,3 +4719,1 +4720,1 +4721,3 +4722,2 +4723,2 +4724,3 +4725,0 +4726,2 +4727,0 +4728,3 +4729,1 +4730,0 +4731,0 +4732,0 +4733,3 +4734,0 +4735,0 +4736,3 +4737,0 +4738,0 +4739,2 +4740,3 +4741,0 +4742,2 +4743,2 +4744,0 +4745,3 +4746,0 +4747,0 +4748,2 +4749,0 +4750,2 +4751,3 +4752,1 +4753,1 +4754,0 +4755,2 +4756,1 +4757,2 +4758,0 +4759,1 +4760,3 +4761,3 +4762,2 +4763,0 +4764,0 +4765,2 +4766,3 +4767,2 +4768,0 +4769,3 +4770,0 +4771,0 +4772,2 +4773,1 +4774,3 +4775,0 +4776,2 +4777,0 +4778,0 +4779,3 +4780,1 +4781,0 +4782,2 +4783,0 +4784,0 +4785,1 +4786,0 +4787,0 +4788,1 +4789,0 +4790,0 +4791,0 +4792,0 +4793,0 +4794,3 +4795,2 +4796,0 +4797,2 +4798,1 +4799,0 +4800,0 +4801,0 +4802,2 +4803,3 +4804,0 +4805,0 +4806,0 +4807,0 +4808,3 +4809,0 +4810,1 +4811,3 +4812,0 +4813,2 +4814,2 +4815,0 +4816,0 +4817,0 +4818,0 +4819,3 +4820,0 +4821,0 +4822,2 +4823,0 +4824,0 +4825,0 +4826,1 +4827,3 +4828,2 +4829,3 +4830,3 +4831,0 +4832,0 +4833,2 +4834,3 +4835,1 +4836,1 +4837,2 +4838,0 +4839,3 +4840,3 +4841,2 +4842,3 +4843,0 +4844,3 +4845,0 +4846,0 +4847,0 +4848,3 +4849,0 +4850,0 +4851,0 +4852,1 +4853,2 +4854,1 +4855,2 +4856,1 +4857,0 +4858,0 +4859,0 +4860,3 +4861,2 +4862,0 +4863,3 +4864,0 +4865,0 +4866,1 +4867,2 +4868,1 +4869,3 +4870,1 +4871,1 +4872,0 +4873,0 +4874,3 +4875,3 +4876,3 +4877,3 +4878,1 +4879,1 +4880,0 +4881,1 +4882,3 +4883,2 +4884,1 +4885,0 +4886,0 +4887,1 +4888,0 +4889,0 +4890,2 +4891,3 +4892,3 +4893,2 +4894,0 +4895,2 +4896,1 +4897,0 +4898,1 +4899,3 +4900,2 +4901,0 +4902,0 +4903,0 +4904,1 +4905,2 +4906,3 +4907,0 +4908,0 +4909,0 +4910,0 +4911,1 +4912,2 +4913,3 +4914,1 +4915,2 +4916,3 +4917,3 +4918,3 +4919,3 +4920,0 +4921,0 +4922,2 +4923,0 +4924,3 +4925,1 +4926,2 +4927,0 +4928,0 +4929,2 +4930,0 +4931,0 +4932,3 +4933,3 +4934,1 +4935,1 +4936,3 +4937,3 +4938,1 +4939,0 +4940,0 +4941,3 +4942,2 +4943,0 +4944,3 +4945,2 +4946,0 +4947,0 +4948,3 +4949,0 +4950,0 +4951,3 +4952,0 +4953,0 +4954,0 +4955,3 +4956,3 +4957,1 +4958,0 +4959,1 +4960,1 +4961,0 +4962,0 +4963,0 +4964,2 +4965,0 +4966,0 +4967,0 +4968,1 +4969,0 +4970,0 +4971,0 +4972,1 +4973,2 +4974,0 +4975,0 +4976,0 +4977,0 +4978,3 +4979,2 +4980,0 +4981,0 +4982,3 +4983,0 +4984,0 +4985,3 +4986,2 +4987,0 +4988,0 +4989,2 +4990,0 +4991,0 +4992,0 +4993,0 +4994,0 +4995,0 +4996,3 +4997,0 +4998,2 +4999,3 +5000,0 +5001,0 +5002,0 +5003,1 +5004,0 +5005,0 +5006,0 +5007,0 +5008,0 +5009,0 +5010,2 +5011,1 +5012,0 +5013,2 +5014,3 +5015,2 +5016,0 +5017,0 +5018,0 +5019,0 +5020,3 +5021,3 +5022,0 +5023,0 +5024,2 +5025,3 +5026,0 +5027,3 +5028,3 +5029,3 +5030,0 +5031,0 +5032,3 +5033,0 +5034,0 +5035,1 +5036,0 +5037,1 +5038,3 +5039,0 +5040,0 +5041,3 +5042,2 +5043,3 +5044,1 +5045,2 +5046,0 +5047,0 +5048,0 +5049,0 +5050,0 +5051,2 +5052,0 +5053,1 +5054,0 +5055,0 +5056,0 +5057,3 +5058,1 +5059,2 +5060,2 +5061,2 +5062,2 +5063,1 +5064,1 +5065,2 +5066,3 +5067,0 +5068,2 +5069,0 +5070,3 +5071,0 +5072,0 +5073,0 +5074,0 +5075,1 +5076,1 +5077,0 +5078,2 +5079,2 +5080,0 +5081,3 +5082,0 +5083,0 +5084,0 +5085,2 +5086,0 +5087,0 +5088,0 +5089,3 +5090,0 +5091,0 +5092,0 +5093,2 +5094,0 +5095,0 +5096,1 +5097,0 +5098,0 +5099,1 +5100,3 +5101,3 +5102,0 +5103,0 +5104,0 +5105,3 +5106,0 +5107,2 +5108,3 +5109,0 +5110,0 +5111,3 +5112,2 +5113,1 +5114,3 +5115,2 +5116,1 +5117,0 +5118,0 +5119,2 +5120,2 +5121,2 +5122,1 +5123,0 +5124,0 +5125,0 +5126,0 +5127,2 +5128,0 +5129,0 +5130,0 +5131,1 +5132,0 +5133,1 +5134,0 +5135,0 +5136,1 +5137,0 +5138,0 +5139,0 +5140,3 +5141,0 +5142,2 +5143,2 +5144,2 +5145,1 +5146,3 +5147,1 +5148,0 +5149,0 +5150,0 +5151,2 +5152,2 +5153,0 +5154,0 +5155,0 +5156,0 +5157,2 +5158,3 +5159,0 +5160,1 +5161,2 +5162,2 +5163,0 +5164,0 +5165,0 +5166,0 +5167,0 +5168,0 +5169,0 +5170,0 +5171,0 +5172,3 +5173,0 +5174,0 +5175,1 +5176,0 +5177,3 +5178,2 +5179,1 +5180,3 +5181,0 +5182,2 +5183,1 +5184,1 +5185,0 +5186,0 +5187,0 +5188,3 +5189,2 +5190,0 +5191,0 +5192,0 +5193,1 +5194,0 +5195,0 +5196,2 +5197,2 +5198,0 +5199,3 +5200,3 +5201,0 +5202,0 +5203,3 +5204,3 +5205,1 +5206,2 +5207,3 +5208,0 +5209,1 +5210,3 +5211,0 +5212,2 +5213,0 +5214,0 +5215,0 +5216,3 +5217,3 +5218,2 +5219,2 +5220,0 +5221,2 +5222,1 +5223,0 +5224,1 +5225,3 +5226,0 +5227,0 +5228,2 +5229,0 +5230,3 +5231,0 +5232,2 +5233,3 +5234,1 +5235,1 +5236,0 +5237,0 +5238,2 +5239,0 +5240,0 +5241,0 +5242,2 +5243,3 +5244,0 +5245,3 +5246,3 +5247,2 +5248,0 +5249,0 +5250,3 +5251,0 +5252,3 +5253,3 +5254,2 +5255,0 +5256,2 +5257,0 +5258,0 +5259,0 +5260,3 +5261,2 +5262,0 +5263,0 +5264,1 +5265,3 +5266,0 +5267,0 +5268,0 +5269,1 +5270,3 +5271,2 +5272,3 +5273,0 +5274,3 +5275,2 +5276,0 +5277,0 +5278,3 +5279,2 +5280,3 +5281,0 +5282,1 +5283,0 +5284,3 +5285,0 +5286,0 +5287,0 +5288,1 +5289,2 +5290,1 +5291,1 +5292,0 +5293,0 +5294,2 +5295,2 +5296,0 +5297,0 +5298,0 +5299,3 +5300,0 +5301,0 +5302,0 +5303,0 +5304,0 +5305,2 +5306,1 +5307,3 +5308,0 +5309,0 +5310,0 +5311,2 +5312,0 +5313,0 +5314,0 +5315,3 +5316,2 +5317,1 +5318,3 +5319,3 +5320,2 +5321,0 +5322,0 +5323,0 +5324,3 +5325,2 +5326,0 +5327,0 +5328,1 +5329,1 +5330,0 +5331,0 +5332,0 +5333,2 +5334,3 +5335,2 +5336,0 +5337,0 +5338,1 +5339,2 +5340,2 +5341,0 +5342,0 +5343,3 +5344,0 +5345,1 +5346,1 +5347,0 +5348,1 +5349,0 +5350,0 +5351,1 +5352,0 +5353,3 +5354,3 +5355,3 +5356,3 +5357,2 +5358,3 +5359,0 +5360,0 +5361,3 +5362,1 +5363,1 +5364,0 +5365,3 +5366,2 +5367,0 +5368,0 +5369,3 +5370,3 +5371,2 +5372,2 +5373,0 +5374,3 +5375,0 +5376,3 +5377,2 +5378,0 +5379,0 +5380,0 +5381,0 +5382,0 +5383,0 +5384,0 +5385,0 +5386,0 +5387,0 +5388,2 +5389,3 +5390,0 +5391,1 +5392,3 +5393,1 +5394,0 +5395,1 +5396,3 +5397,0 +5398,2 +5399,0 +5400,3 +5401,3 +5402,2 +5403,1 +5404,0 +5405,0 +5406,0 +5407,0 +5408,3 +5409,2 +5410,3 +5411,1 +5412,3 +5413,3 +5414,0 +5415,2 +5416,2 +5417,3 +5418,3 +5419,0 +5420,2 +5421,1 +5422,1 +5423,3 +5424,0 +5425,1 +5426,3 +5427,0 +5428,0 +5429,0 +5430,1 +5431,2 +5432,0 +5433,1 +5434,0 +5435,3 +5436,0 +5437,0 +5438,1 +5439,3 +5440,1 +5441,3 +5442,3 +5443,3 +5444,1 +5445,0 +5446,2 +5447,0 +5448,0 +5449,1 +5450,0 +5451,2 +5452,3 +5453,1 +5454,0 +5455,0 +5456,0 +5457,0 +5458,0 +5459,0 +5460,2 +5461,0 +5462,1 +5463,0 +5464,0 +5465,0 +5466,0 +5467,0 +5468,1 +5469,0 +5470,0 +5471,0 +5472,0 +5473,0 +5474,0 +5475,1 +5476,3 +5477,2 +5478,0 +5479,0 +5480,0 +5481,2 +5482,2 +5483,1 +5484,0 +5485,0 +5486,1 +5487,0 +5488,0 +5489,3 +5490,0 +5491,0 +5492,2 +5493,2 +5494,1 +5495,0 +5496,0 +5497,0 +5498,0 +5499,3 +5500,2 +5501,0 +5502,0 +5503,0 +5504,3 +5505,3 +5506,3 +5507,0 +5508,3 +5509,3 +5510,3 +5511,2 +5512,3 +5513,0 +5514,3 +5515,3 +5516,3 +5517,3 +5518,2 +5519,0 +5520,0 +5521,3 +5522,0 +5523,3 +5524,3 +5525,3 +5526,3 +5527,3 +5528,1 +5529,0 +5530,2 +5531,0 +5532,0 +5533,3 +5534,0 +5535,1 +5536,2 +5537,3 +5538,0 +5539,0 +5540,2 +5541,2 +5542,0 +5543,1 +5544,2 +5545,0 +5546,0 +5547,3 +5548,3 +5549,0 +5550,3 +5551,0 +5552,1 +5553,0 +5554,0 +5555,0 +5556,1 +5557,0 +5558,0 +5559,0 +5560,0 +5561,1 +5562,0 +5563,3 +5564,2 +5565,1 +5566,3 +5567,2 +5568,0 +5569,2 +5570,0 +5571,0 +5572,0 +5573,2 +5574,0 +5575,2 +5576,1 +5577,0 +5578,3 +5579,0 +5580,0 +5581,0 +5582,0 +5583,3 +5584,1 +5585,3 +5586,1 +5587,0 +5588,0 +5589,1 +5590,3 +5591,3 +5592,2 +5593,2 +5594,2 +5595,2 +5596,1 +5597,0 +5598,0 +5599,1 +5600,0 +5601,1 +5602,2 +5603,3 +5604,3 +5605,2 +5606,3 +5607,3 +5608,2 +5609,0 +5610,2 +5611,3 +5612,1 +5613,2 +5614,3 +5615,0 +5616,0 +5617,0 +5618,1 +5619,1 +5620,3 +5621,0 +5622,3 +5623,0 +5624,0 +5625,0 +5626,3 +5627,3 +5628,3 +5629,2 +5630,2 +5631,2 +5632,0 +5633,0 +5634,2 +5635,0 +5636,0 +5637,0 +5638,2 +5639,0 +5640,0 +5641,2 +5642,0 +5643,0 +5644,0 +5645,3 +5646,2 +5647,2 +5648,1 +5649,3 +5650,2 +5651,1 +5652,0 +5653,0 +5654,0 +5655,1 +5656,3 +5657,0 +5658,3 +5659,0 +5660,0 +5661,0 +5662,3 +5663,2 +5664,3 +5665,0 +5666,2 +5667,1 +5668,2 +5669,0 +5670,2 +5671,0 +5672,0 +5673,3 +5674,1 +5675,2 +5676,3 +5677,3 +5678,1 +5679,3 +5680,0 +5681,1 +5682,0 +5683,0 +5684,0 +5685,0 +5686,1 +5687,3 +5688,0 +5689,1 +5690,0 +5691,0 +5692,3 +5693,2 +5694,0 +5695,0 +5696,0 +5697,2 +5698,3 +5699,0 +5700,2 +5701,0 +5702,2 +5703,3 +5704,0 +5705,0 +5706,0 +5707,0 +5708,0 +5709,0 +5710,3 +5711,2 +5712,0 +5713,0 +5714,0 +5715,0 +5716,2 +5717,0 +5718,2 +5719,0 +5720,0 +5721,0 +5722,2 +5723,1 +5724,3 +5725,2 +5726,0 +5727,3 +5728,3 +5729,0 +5730,0 +5731,0 +5732,0 +5733,0 +5734,1 +5735,0 +5736,3 +5737,3 +5738,0 +5739,0 +5740,0 +5741,0 +5742,3 +5743,0 +5744,1 +5745,0 +5746,1 +5747,3 +5748,1 +5749,0 +5750,0 +5751,1 +5752,0 +5753,0 +5754,3 +5755,0 +5756,0 +5757,3 +5758,0 +5759,0 +5760,0 +5761,2 +5762,3 +5763,1 +5764,1 +5765,0 +5766,0 +5767,2 +5768,3 +5769,0 +5770,2 +5771,0 +5772,2 +5773,1 +5774,0 +5775,0 +5776,1 +5777,0 +5778,0 +5779,3 +5780,2 +5781,0 +5782,3 +5783,2 +5784,3 +5785,0 +5786,1 +5787,2 +5788,3 +5789,0 +5790,0 +5791,2 +5792,0 +5793,2 +5794,2 +5795,1 +5796,0 +5797,3 +5798,2 +5799,0 +5800,0 +5801,2 +5802,3 +5803,3 +5804,1 +5805,3 +5806,3 +5807,3 +5808,1 +5809,1 +5810,0 +5811,0 +5812,0 +5813,0 +5814,1 +5815,0 +5816,1 +5817,3 +5818,3 +5819,3 +5820,0 +5821,1 +5822,0 +5823,1 +5824,3 +5825,0 +5826,1 +5827,0 +5828,0 +5829,0 +5830,0 +5831,3 +5832,0 +5833,2 +5834,0 +5835,2 +5836,3 +5837,2 +5838,2 +5839,0 +5840,2 +5841,2 +5842,1 +5843,0 +5844,1 +5845,2 +5846,0 +5847,2 +5848,1 +5849,0 +5850,1 +5851,1 +5852,2 +5853,2 +5854,0 +5855,3 +5856,0 +5857,2 +5858,0 +5859,1 +5860,3 +5861,3 +5862,3 +5863,3 +5864,1 +5865,2 +5866,0 +5867,2 +5868,0 +5869,0 +5870,2 +5871,1 +5872,1 +5873,0 +5874,0 +5875,0 +5876,2 +5877,2 +5878,2 +5879,0 +5880,1 +5881,1 +5882,1 +5883,0 +5884,2 +5885,0 +5886,3 +5887,0 +5888,0 +5889,0 +5890,2 +5891,1 +5892,0 +5893,0 +5894,3 +5895,1 +5896,0 +5897,0 +5898,2 +5899,0 +5900,0 +5901,3 +5902,1 +5903,2 +5904,0 +5905,3 +5906,0 +5907,3 +5908,0 +5909,3 +5910,0 +5911,3 +5912,3 +5913,2 +5914,0 +5915,0 +5916,0 +5917,3 +5918,2 +5919,0 +5920,1 +5921,3 +5922,0 +5923,0 +5924,3 +5925,0 +5926,2 +5927,2 +5928,0 +5929,0 +5930,0 +5931,2 +5932,0 +5933,0 +5934,3 +5935,0 +5936,0 +5937,2 +5938,0 +5939,2 +5940,1 +5941,1 +5942,1 +5943,3 +5944,0 +5945,1 +5946,0 +5947,2 +5948,3 +5949,0 +5950,2 +5951,2 +5952,1 +5953,2 +5954,0 +5955,0 +5956,3 +5957,1 +5958,0 +5959,0 +5960,3 +5961,0 +5962,2 +5963,0 +5964,0 +5965,0 +5966,0 +5967,0 +5968,3 +5969,0 +5970,3 +5971,0 +5972,2 +5973,1 +5974,1 +5975,0 +5976,0 +5977,1 +5978,2 +5979,3 +5980,2 +5981,1 +5982,0 +5983,3 +5984,3 +5985,2 +5986,0 +5987,1 +5988,2 +5989,0 +5990,0 +5991,1 +5992,3 +5993,0 +5994,0 +5995,0 +5996,2 +5997,2 +5998,0 +5999,3 +6000,0 +6001,3 +6002,1 +6003,0 +6004,3 +6005,1 +6006,3 +6007,0 +6008,0 +6009,2 +6010,3 +6011,2 +6012,0 +6013,3 +6014,0 +6015,3 +6016,3 +6017,3 +6018,0 +6019,2 +6020,0 +6021,0 +6022,2 +6023,3 +6024,3 +6025,1 +6026,0 +6027,0 +6028,0 +6029,0 +6030,3 +6031,0 +6032,0 +6033,3 +6034,0 +6035,2 +6036,0 +6037,0 +6038,2 +6039,0 +6040,2 +6041,2 +6042,2 +6043,1 +6044,0 +6045,1 +6046,2 +6047,1 +6048,1 +6049,0 +6050,1 +6051,0 +6052,3 +6053,0 +6054,0 +6055,0 +6056,3 +6057,1 +6058,2 +6059,2 +6060,0 +6061,0 +6062,3 +6063,3 +6064,3 +6065,2 +6066,0 +6067,1 +6068,2 +6069,0 +6070,0 +6071,2 +6072,0 +6073,2 +6074,2 +6075,1 +6076,0 +6077,1 +6078,0 +6079,2 +6080,3 +6081,2 +6082,0 +6083,0 +6084,0 +6085,2 +6086,2 +6087,1 +6088,1 +6089,3 +6090,3 +6091,0 +6092,0 +6093,0 +6094,1 +6095,3 +6096,0 +6097,3 +6098,1 +6099,2 +6100,0 +6101,2 +6102,0 +6103,2 +6104,0 +6105,0 +6106,0 +6107,0 +6108,0 +6109,0 +6110,2 +6111,1 +6112,0 +6113,0 +6114,2 +6115,3 +6116,0 +6117,0 +6118,0 +6119,0 +6120,0 +6121,2 +6122,3 +6123,0 +6124,3 +6125,2 +6126,0 +6127,2 +6128,0 +6129,0 +6130,1 +6131,0 +6132,0 +6133,0 +6134,0 +6135,0 +6136,0 +6137,3 +6138,0 +6139,2 +6140,2 +6141,0 +6142,2 +6143,2 +6144,3 +6145,3 +6146,0 +6147,1 +6148,0 +6149,0 +6150,0 +6151,2 +6152,0 +6153,2 +6154,0 +6155,3 +6156,1 +6157,3 +6158,2 +6159,0 +6160,0 +6161,0 +6162,0 +6163,0 +6164,3 +6165,0 +6166,1 +6167,0 +6168,2 +6169,0 +6170,0 +6171,2 +6172,0 +6173,2 +6174,0 +6175,3 +6176,0 +6177,2 +6178,2 +6179,2 +6180,1 +6181,3 +6182,3 +6183,0 +6184,0 +6185,2 +6186,0 +6187,0 +6188,0 +6189,0 +6190,0 +6191,3 +6192,2 +6193,0 +6194,2 +6195,3 +6196,0 +6197,2 +6198,0 +6199,0 +6200,0 +6201,1 +6202,2 +6203,0 +6204,0 +6205,0 +6206,0 +6207,1 +6208,3 +6209,2 +6210,2 +6211,1 +6212,3 +6213,2 +6214,0 +6215,3 +6216,3 +6217,1 +6218,1 +6219,0 +6220,0 +6221,0 +6222,0 +6223,0 +6224,1 +6225,0 +6226,1 +6227,3 +6228,1 +6229,0 +6230,0 +6231,0 +6232,0 +6233,0 +6234,2 +6235,3 +6236,0 +6237,0 +6238,0 +6239,0 +6240,0 +6241,0 +6242,0 +6243,1 +6244,0 +6245,2 +6246,2 +6247,0 +6248,0 +6249,0 +6250,3 +6251,1 +6252,1 +6253,0 +6254,0 +6255,0 +6256,3 +6257,0 +6258,0 +6259,3 +6260,0 +6261,0 +6262,2 +6263,2 +6264,3 +6265,3 +6266,3 +6267,0 +6268,0 +6269,0 +6270,0 +6271,2 +6272,2 +6273,0 +6274,3 +6275,2 +6276,2 +6277,0 +6278,0 +6279,1 +6280,1 +6281,2 +6282,0 +6283,0 +6284,1 +6285,0 +6286,0 +6287,3 +6288,2 +6289,1 +6290,0 +6291,3 +6292,0 +6293,0 +6294,3 +6295,0 +6296,1 +6297,0 +6298,0 +6299,2 +6300,3 +6301,3 +6302,2 +6303,0 +6304,2 +6305,0 +6306,0 +6307,0 +6308,0 +6309,2 +6310,1 +6311,3 +6312,2 +6313,0 +6314,3 +6315,3 +6316,0 +6317,2 +6318,3 +6319,3 +6320,3 +6321,3 +6322,0 +6323,2 +6324,2 +6325,0 +6326,3 +6327,0 +6328,2 +6329,0 +6330,0 +6331,0 +6332,3 +6333,0 +6334,3 +6335,0 +6336,3 +6337,0 +6338,0 +6339,0 +6340,3 +6341,0 +6342,0 +6343,2 +6344,0 +6345,1 +6346,1 +6347,3 +6348,2 +6349,1 +6350,0 +6351,0 +6352,3 +6353,2 +6354,0 +6355,0 +6356,0 +6357,3 +6358,3 +6359,2 +6360,0 +6361,0 +6362,3 +6363,2 +6364,3 +6365,3 +6366,2 +6367,1 +6368,0 +6369,3 +6370,2 +6371,3 +6372,0 +6373,3 +6374,0 +6375,0 +6376,2 +6377,0 +6378,0 +6379,0 +6380,2 +6381,1 +6382,3 +6383,2 +6384,0 +6385,3 +6386,2 +6387,0 +6388,0 +6389,0 +6390,3 +6391,3 +6392,3 +6393,0 +6394,3 +6395,0 +6396,3 +6397,0 +6398,0 +6399,1 +6400,1 +6401,0 +6402,1 +6403,0 +6404,3 +6405,3 +6406,2 +6407,0 +6408,2 +6409,3 +6410,1 +6411,2 +6412,2 +6413,0 +6414,0 +6415,0 +6416,2 +6417,0 +6418,0 +6419,1 +6420,0 +6421,2 +6422,3 +6423,2 +6424,0 +6425,2 +6426,1 +6427,0 +6428,0 +6429,0 +6430,2 +6431,0 +6432,2 +6433,1 +6434,2 +6435,1 +6436,1 +6437,2 +6438,3 +6439,1 +6440,3 +6441,2 +6442,2 +6443,0 +6444,1 +6445,0 +6446,3 +6447,0 +6448,0 +6449,2 +6450,3 +6451,0 +6452,3 +6453,0 +6454,0 +6455,0 +6456,0 +6457,0 +6458,3 +6459,2 +6460,0 +6461,3 +6462,2 +6463,0 +6464,2 +6465,0 +6466,0 +6467,1 +6468,1 +6469,1 +6470,0 +6471,0 +6472,0 +6473,2 +6474,0 +6475,1 +6476,2 +6477,3 +6478,0 +6479,2 +6480,3 +6481,0 +6482,2 +6483,2 +6484,2 +6485,3 +6486,0 +6487,1 +6488,0 +6489,0 +6490,0 +6491,0 +6492,0 +6493,2 +6494,3 +6495,0 +6496,2 +6497,0 +6498,2 +6499,3 +6500,3 +6501,1 +6502,0 +6503,3 +6504,0 +6505,0 +6506,3 +6507,0 +6508,3 +6509,3 +6510,0 +6511,2 +6512,3 +6513,3 +6514,1 +6515,1 +6516,2 +6517,1 +6518,2 +6519,0 +6520,2 +6521,3 +6522,0 +6523,0 +6524,1 +6525,0 +6526,3 +6527,3 +6528,0 +6529,1 +6530,1 +6531,0 +6532,0 +6533,0 +6534,0 +6535,0 +6536,3 +6537,0 +6538,2 +6539,2 +6540,0 +6541,0 +6542,0 +6543,0 +6544,0 +6545,1 +6546,3 +6547,0 +6548,0 +6549,1 +6550,1 +6551,0 +6552,0 +6553,2 +6554,2 +6555,0 +6556,2 +6557,2 +6558,1 +6559,3 +6560,2 +6561,1 +6562,1 +6563,2 +6564,0 +6565,0 +6566,3 +6567,3 +6568,0 +6569,2 +6570,1 +6571,2 +6572,0 +6573,0 +6574,0 +6575,3 +6576,0 +6577,3 +6578,0 +6579,0 +6580,1 +6581,0 +6582,3 +6583,0 +6584,1 +6585,0 +6586,0 +6587,0 +6588,3 +6589,0 +6590,0 +6591,0 +6592,0 +6593,3 +6594,0 +6595,0 +6596,0 +6597,2 +6598,2 +6599,3 +6600,3 +6601,0 +6602,0 +6603,0 +6604,3 +6605,3 +6606,3 +6607,2 +6608,1 +6609,0 +6610,0 +6611,1 +6612,2 +6613,3 +6614,2 +6615,0 +6616,2 +6617,3 +6618,2 +6619,0 +6620,0 +6621,0 +6622,3 +6623,0 +6624,0 +6625,1 +6626,0 +6627,3 +6628,0 +6629,0 +6630,0 +6631,3 +6632,2 +6633,2 +6634,0 +6635,1 +6636,0 +6637,2 +6638,2 +6639,0 +6640,3 +6641,1 +6642,0 +6643,3 +6644,2 +6645,0 +6646,0 +6647,0 +6648,3 +6649,3 +6650,0 +6651,0 +6652,0 +6653,3 +6654,0 +6655,0 +6656,0 +6657,0 +6658,2 +6659,1 +6660,3 +6661,3 +6662,2 +6663,0 +6664,0 +6665,0 +6666,0 +6667,0 +6668,0 +6669,3 +6670,0 +6671,3 +6672,0 +6673,0 +6674,0 +6675,1 +6676,2 +6677,0 +6678,2 +6679,2 +6680,0 +6681,0 +6682,0 +6683,0 +6684,3 +6685,2 +6686,2 +6687,1 +6688,2 +6689,0 +6690,0 +6691,2 +6692,3 +6693,1 +6694,3 +6695,0 +6696,2 +6697,0 +6698,3 +6699,0 +6700,2 +6701,0 +6702,3 +6703,1 +6704,0 +6705,2 +6706,1 +6707,2 +6708,1 +6709,3 +6710,0 +6711,2 +6712,0 +6713,2 +6714,0 +6715,0 +6716,3 +6717,3 +6718,2 +6719,2 +6720,2 +6721,0 +6722,0 +6723,0 +6724,0 +6725,0 +6726,0 +6727,1 +6728,1 +6729,0 +6730,0 +6731,1 +6732,2 +6733,0 +6734,0 +6735,2 +6736,2 +6737,0 +6738,0 +6739,0 +6740,0 +6741,1 +6742,1 +6743,1 +6744,0 +6745,3 +6746,3 +6747,0 +6748,1 +6749,0 +6750,0 +6751,3 +6752,0 +6753,0 +6754,3 +6755,0 +6756,3 +6757,2 +6758,1 +6759,3 +6760,0 +6761,3 +6762,2 +6763,0 +6764,3 +6765,0 +6766,3 +6767,3 +6768,0 +6769,3 +6770,2 +6771,2 +6772,2 +6773,2 +6774,0 +6775,3 +6776,0 +6777,0 +6778,1 +6779,2 +6780,3 +6781,3 +6782,2 +6783,1 +6784,2 +6785,0 +6786,3 +6787,3 +6788,1 +6789,0 +6790,2 +6791,0 +6792,2 +6793,2 +6794,0 +6795,0 +6796,0 +6797,0 +6798,0 +6799,0 +6800,0 +6801,0 +6802,0 +6803,0 +6804,0 +6805,0 +6806,0 +6807,3 +6808,0 +6809,1 +6810,0 +6811,1 +6812,0 +6813,2 +6814,3 +6815,1 +6816,2 +6817,1 +6818,3 +6819,0 +6820,1 +6821,1 +6822,3 +6823,0 +6824,0 +6825,2 +6826,2 +6827,3 +6828,0 +6829,0 +6830,3 +6831,0 +6832,0 +6833,2 +6834,1 +6835,0 +6836,3 +6837,2 +6838,0 +6839,3 +6840,0 +6841,0 +6842,3 +6843,1 +6844,0 +6845,3 +6846,0 +6847,0 +6848,0 +6849,3 +6850,2 +6851,0 +6852,2 +6853,3 +6854,3 +6855,0 +6856,0 +6857,3 +6858,3 +6859,0 +6860,0 +6861,3 +6862,3 +6863,0 +6864,0 +6865,1 +6866,2 +6867,3 +6868,2 +6869,1 +6870,1 +6871,3 +6872,2 +6873,0 +6874,1 +6875,0 +6876,0 +6877,2 +6878,0 +6879,1 +6880,3 +6881,3 +6882,3 +6883,1 +6884,2 +6885,0 +6886,0 +6887,0 +6888,0 +6889,1 +6890,3 +6891,0 +6892,1 +6893,3 +6894,3 +6895,3 +6896,1 +6897,1 +6898,0 +6899,1 +6900,2 +6901,2 +6902,0 +6903,1 +6904,0 +6905,2 +6906,2 +6907,0 +6908,0 +6909,2 +6910,0 +6911,0 +6912,0 +6913,1 +6914,3 +6915,1 +6916,3 +6917,1 +6918,3 +6919,2 +6920,0 +6921,1 +6922,0 +6923,1 +6924,0 +6925,2 +6926,2 +6927,0 +6928,2 +6929,1 +6930,0 +6931,1 +6932,0 +6933,3 +6934,3 +6935,3 +6936,3 +6937,0 +6938,0 +6939,0 +6940,2 +6941,0 +6942,1 +6943,0 +6944,0 +6945,0 +6946,2 +6947,3 +6948,2 +6949,1 +6950,3 +6951,2 +6952,2 +6953,2 +6954,0 +6955,1 +6956,0 +6957,2 +6958,0 +6959,0 +6960,0 +6961,3 +6962,2 +6963,0 +6964,3 +6965,1 +6966,2 +6967,0 +6968,0 +6969,3 +6970,3 +6971,0 +6972,0 +6973,0 +6974,0 +6975,0 +6976,0 +6977,0 +6978,3 +6979,0 +6980,0 +6981,0 +6982,0 +6983,2 +6984,3 +6985,3 +6986,3 +6987,0 +6988,0 +6989,1 +6990,0 +6991,2 +6992,2 +6993,3 +6994,2 +6995,0 +6996,1 +6997,1 +6998,2 +6999,1 +7000,2 +7001,2 +7002,0 +7003,3 +7004,0 +7005,1 +7006,0 +7007,0 +7008,0 +7009,0 +7010,2 +7011,1 +7012,0 +7013,0 +7014,0 +7015,0 +7016,2 +7017,0 +7018,0 +7019,0 +7020,2 +7021,0 +7022,0 +7023,2 +7024,0 +7025,1 +7026,0 +7027,0 +7028,2 +7029,1 +7030,0 +7031,2 +7032,3 +7033,3 +7034,0 +7035,3 +7036,3 +7037,2 +7038,0 +7039,0 +7040,0 +7041,2 +7042,0 +7043,0 +7044,3 +7045,0 +7046,0 +7047,3 +7048,1 +7049,2 +7050,1 +7051,1 +7052,1 +7053,0 +7054,0 +7055,0 +7056,0 +7057,0 +7058,1 +7059,0 +7060,0 +7061,2 +7062,2 +7063,0 +7064,0 +7065,2 +7066,0 +7067,2 +7068,2 +7069,0 +7070,0 +7071,2 +7072,2 +7073,2 +7074,2 +7075,0 +7076,0 +7077,0 +7078,3 +7079,3 +7080,2 +7081,2 +7082,1 +7083,0 +7084,2 +7085,0 +7086,1 +7087,2 +7088,2 +7089,1 +7090,0 +7091,2 +7092,0 +7093,3 +7094,2 +7095,3 +7096,0 +7097,0 +7098,0 +7099,2 +7100,1 +7101,3 +7102,0 +7103,2 +7104,2 +7105,0 +7106,1 +7107,2 +7108,3 +7109,3 +7110,0 +7111,1 +7112,2 +7113,0 +7114,3 +7115,0 +7116,0 +7117,3 +7118,0 +7119,0 +7120,0 +7121,3 +7122,0 +7123,0 +7124,3 +7125,2 +7126,2 +7127,1 +7128,0 +7129,0 +7130,0 +7131,2 +7132,2 +7133,0 +7134,0 +7135,1 +7136,1 +7137,0 +7138,0 +7139,2 +7140,1 +7141,1 +7142,0 +7143,3 +7144,0 +7145,3 +7146,0 +7147,2 +7148,0 +7149,0 +7150,3 +7151,3 +7152,3 +7153,0 +7154,2 +7155,0 +7156,2 +7157,3 +7158,3 +7159,0 +7160,1 +7161,1 +7162,1 +7163,2 +7164,3 +7165,0 +7166,1 +7167,0 +7168,0 +7169,0 +7170,2 +7171,0 +7172,0 +7173,3 +7174,3 +7175,3 +7176,2 +7177,2 +7178,0 +7179,3 +7180,0 +7181,2 +7182,1 +7183,3 +7184,0 +7185,0 +7186,0 +7187,3 +7188,0 +7189,3 +7190,0 +7191,3 +7192,0 +7193,2 +7194,3 +7195,0 +7196,2 +7197,0 +7198,0 +7199,0 +7200,1 +7201,0 +7202,3 +7203,0 +7204,0 +7205,0 +7206,2 +7207,0 +7208,0 +7209,2 +7210,1 +7211,3 +7212,3 +7213,3 +7214,0 +7215,0 +7216,0 +7217,1 +7218,0 +7219,1 +7220,1 +7221,0 +7222,1 +7223,2 +7224,0 +7225,0 +7226,3 +7227,3 +7228,2 +7229,0 +7230,2 +7231,2 +7232,2 +7233,0 +7234,0 +7235,0 +7236,1 +7237,0 +7238,1 +7239,3 +7240,1 +7241,3 +7242,3 +7243,2 +7244,2 +7245,3 +7246,2 +7247,2 +7248,0 +7249,1 +7250,0 +7251,2 +7252,1 +7253,2 +7254,2 +7255,2 +7256,2 +7257,3 +7258,0 +7259,0 +7260,0 +7261,0 +7262,2 +7263,1 +7264,0 +7265,0 +7266,1 +7267,0 +7268,0 +7269,3 +7270,1 +7271,2 +7272,0 +7273,3 +7274,2 +7275,1 +7276,3 +7277,0 +7278,0 +7279,2 +7280,1 +7281,3 +7282,1 +7283,0 +7284,3 +7285,0 +7286,1 +7287,3 +7288,0 +7289,1 +7290,3 +7291,0 +7292,1 +7293,0 +7294,2 +7295,2 +7296,2 +7297,0 +7298,0 +7299,1 +7300,2 +7301,1 +7302,2 +7303,0 +7304,1 +7305,0 +7306,3 +7307,0 +7308,1 +7309,0 +7310,0 +7311,2 +7312,3 +7313,1 +7314,3 +7315,0 +7316,1 +7317,1 +7318,1 +7319,0 +7320,0 +7321,2 +7322,0 +7323,3 +7324,0 +7325,0 +7326,0 +7327,2 +7328,3 +7329,0 +7330,0 +7331,0 +7332,3 +7333,3 +7334,0 +7335,1 +7336,0 +7337,3 +7338,3 +7339,2 +7340,2 +7341,1 +7342,0 +7343,1 +7344,3 +7345,3 +7346,0 +7347,2 +7348,3 +7349,1 +7350,0 +7351,3 +7352,2 +7353,0 +7354,2 +7355,0 +7356,1 +7357,1 +7358,0 +7359,3 +7360,0 +7361,0 +7362,1 +7363,3 +7364,0 +7365,1 +7366,2 +7367,0 +7368,0 +7369,2 +7370,3 +7371,1 +7372,0 +7373,3 +7374,0 +7375,1 +7376,2 +7377,0 +7378,2 +7379,0 +7380,0 +7381,2 +7382,0 +7383,1 +7384,2 +7385,2 +7386,2 +7387,0 +7388,0 +7389,2 +7390,0 +7391,0 +7392,1 +7393,0 +7394,3 +7395,2 +7396,1 +7397,2 +7398,0 +7399,3 +7400,2 +7401,2 +7402,0 +7403,1 +7404,0 +7405,0 +7406,1 +7407,0 +7408,0 +7409,2 +7410,2 +7411,3 +7412,0 +7413,0 +7414,0 +7415,3 +7416,3 +7417,0 +7418,2 +7419,1 +7420,0 +7421,0 +7422,3 +7423,3 +7424,1 +7425,0 +7426,0 +7427,0 +7428,3 +7429,0 +7430,3 +7431,2 +7432,3 +7433,2 +7434,0 +7435,1 +7436,0 +7437,0 +7438,0 +7439,2 +7440,0 +7441,2 +7442,3 +7443,3 +7444,1 +7445,0 +7446,0 +7447,0 +7448,0 +7449,0 +7450,1 +7451,2 +7452,3 +7453,0 +7454,0 +7455,0 +7456,3 +7457,0 +7458,2 +7459,0 +7460,0 +7461,0 +7462,0 +7463,0 +7464,3 +7465,3 +7466,2 +7467,0 +7468,0 +7469,3 +7470,3 +7471,3 +7472,2 +7473,1 +7474,0 +7475,0 +7476,1 +7477,2 +7478,0 +7479,0 +7480,0 +7481,0 +7482,3 +7483,0 +7484,2 +7485,3 +7486,2 +7487,2 +7488,0 +7489,0 +7490,0 +7491,0 +7492,2 +7493,0 +7494,3 +7495,0 +7496,0 +7497,3 +7498,0 +7499,3 +7500,0 +7501,0 +7502,0 +7503,3 +7504,1 +7505,0 +7506,0 +7507,0 +7508,0 +7509,2 +7510,3 +7511,0 +7512,3 +7513,3 +7514,1 +7515,2 +7516,1 +7517,2 +7518,0 +7519,3 +7520,1 +7521,3 +7522,0 +7523,3 +7524,0 +7525,1 +7526,0 +7527,3 +7528,0 +7529,0 +7530,2 +7531,2 +7532,0 +7533,2 +7534,0 +7535,2 +7536,3 +7537,2 +7538,0 +7539,0 +7540,0 +7541,2 +7542,0 +7543,0 +7544,0 +7545,2 +7546,0 +7547,0 +7548,3 +7549,0 +7550,2 +7551,0 +7552,0 +7553,2 +7554,3 +7555,0 +7556,0 +7557,0 +7558,3 +7559,1 +7560,0 +7561,2 +7562,0 +7563,3 +7564,3 +7565,3 +7566,2 +7567,0 +7568,0 +7569,0 +7570,0 +7571,3 +7572,0 +7573,2 +7574,2 +7575,1 +7576,3 +7577,0 +7578,1 +7579,0 +7580,0 +7581,0 +7582,2 +7583,1 +7584,0 +7585,0 +7586,0 +7587,1 +7588,0 +7589,0 +7590,0 +7591,0 +7592,2 +7593,0 +7594,3 +7595,0 +7596,2 +7597,1 +7598,1 +7599,0 +7600,3 +7601,3 +7602,0 +7603,0 +7604,1 +7605,0 +7606,3 +7607,2 +7608,0 +7609,0 +7610,3 +7611,0 +7612,0 +7613,0 +7614,0 +7615,3 +7616,2 +7617,0 +7618,0 +7619,1 +7620,0 +7621,1 +7622,0 +7623,3 +7624,0 +7625,1 +7626,3 +7627,0 +7628,3 +7629,0 +7630,0 +7631,0 +7632,1 +7633,3 +7634,0 +7635,1 +7636,0 +7637,1 +7638,2 +7639,3 +7640,2 +7641,2 +7642,0 +7643,2 +7644,0 +7645,2 +7646,3 +7647,1 +7648,2 +7649,0 +7650,1 +7651,0 +7652,2 +7653,1 +7654,2 +7655,0 +7656,0 +7657,3 +7658,0 +7659,0 +7660,2 +7661,0 +7662,0 +7663,3 +7664,2 +7665,0 +7666,2 +7667,0 +7668,0 +7669,3 +7670,2 +7671,0 +7672,0 +7673,2 +7674,2 +7675,2 +7676,0 +7677,0 +7678,1 +7679,3 +7680,0 +7681,1 +7682,3 +7683,0 +7684,2 +7685,0 +7686,0 +7687,2 +7688,0 +7689,1 +7690,0 +7691,1 +7692,0 +7693,2 +7694,3 +7695,3 +7696,0 +7697,0 +7698,0 +7699,2 +7700,2 +7701,2 +7702,3 +7703,0 +7704,0 +7705,2 +7706,0 +7707,3 +7708,3 +7709,0 +7710,2 +7711,0 +7712,0 +7713,2 +7714,3 +7715,1 +7716,3 +7717,0 +7718,3 +7719,3 +7720,0 +7721,0 +7722,0 +7723,1 +7724,0 +7725,2 +7726,0 +7727,0 +7728,0 +7729,1 +7730,0 +7731,3 +7732,3 +7733,0 +7734,2 +7735,1 +7736,2 +7737,1 +7738,1 +7739,0 +7740,3 +7741,3 +7742,0 +7743,0 +7744,3 +7745,0 +7746,0 +7747,3 +7748,1 +7749,3 +7750,0 +7751,3 +7752,0 +7753,2 +7754,0 +7755,0 +7756,1 +7757,1 +7758,1 +7759,1 +7760,3 +7761,0 +7762,2 +7763,0 +7764,0 +7765,0 +7766,3 +7767,2 +7768,3 +7769,3 +7770,0 +7771,3 +7772,0 +7773,0 +7774,3 +7775,2 +7776,1 +7777,3 +7778,1 +7779,3 +7780,3 +7781,0 +7782,0 +7783,1 +7784,2 +7785,1 +7786,2 +7787,3 +7788,1 +7789,0 +7790,3 +7791,0 +7792,2 +7793,3 +7794,0 +7795,3 +7796,2 +7797,2 +7798,0 +7799,0 +7800,0 +7801,3 +7802,3 +7803,3 +7804,0 +7805,3 +7806,1 +7807,0 +7808,3 +7809,0 +7810,3 +7811,0 +7812,2 +7813,1 +7814,0 +7815,2 +7816,0 +7817,1 +7818,2 +7819,3 +7820,0 +7821,0 +7822,0 +7823,3 +7824,0 +7825,3 +7826,0 +7827,3 +7828,3 +7829,1 +7830,0 +7831,0 +7832,0 +7833,1 +7834,0 +7835,0 +7836,3 +7837,0 +7838,0 +7839,1 +7840,1 +7841,0 +7842,2 +7843,0 +7844,0 +7845,0 +7846,3 +7847,0 +7848,0 +7849,0 +7850,2 +7851,0 +7852,0 +7853,1 +7854,0 +7855,3 +7856,1 +7857,0 +7858,0 +7859,3 +7860,0 +7861,0 +7862,0 +7863,0 +7864,3 +7865,0 +7866,1 +7867,0 +7868,3 +7869,3 +7870,0 +7871,0 +7872,0 +7873,2 +7874,3 +7875,3 +7876,2 +7877,0 +7878,2 +7879,0 +7880,2 +7881,1 +7882,0 +7883,0 +7884,2 +7885,0 +7886,0 +7887,2 +7888,2 +7889,1 +7890,0 +7891,3 +7892,1 +7893,2 +7894,0 +7895,0 +7896,0 +7897,1 +7898,2 +7899,3 +7900,0 +7901,0 +7902,1 +7903,1 +7904,0 +7905,1 +7906,2 +7907,0 +7908,0 +7909,3 +7910,1 +7911,2 +7912,2 +7913,2 +7914,0 +7915,3 +7916,3 +7917,0 +7918,0 +7919,0 +7920,1 +7921,0 +7922,3 +7923,0 +7924,1 +7925,0 +7926,1 +7927,2 +7928,1 +7929,0 +7930,0 +7931,1 +7932,3 +7933,2 +7934,1 +7935,0 +7936,1 +7937,0 +7938,0 +7939,0 +7940,3 +7941,0 +7942,0 +7943,1 +7944,2 +7945,0 +7946,2 +7947,0 +7948,0 +7949,2 +7950,0 +7951,0 +7952,0 +7953,1 +7954,0 +7955,3 +7956,3 +7957,3 +7958,3 +7959,2 +7960,3 +7961,0 +7962,1 +7963,3 +7964,0 +7965,0 +7966,0 +7967,0 +7968,1 +7969,0 +7970,2 +7971,2 +7972,2 +7973,3 +7974,0 +7975,3 +7976,0 +7977,2 +7978,1 +7979,1 +7980,0 +7981,0 +7982,2 +7983,3 +7984,3 +7985,0 +7986,0 +7987,2 +7988,2 +7989,0 +7990,3 +7991,2 +7992,0 +7993,0 +7994,0 +7995,1 +7996,1 +7997,2 +7998,1 +7999,0 +8000,0 +8001,0 +8002,1 +8003,3 +8004,0 +8005,3 +8006,2 +8007,2 +8008,1 +8009,1 +8010,2 +8011,3 +8012,0 +8013,3 +8014,3 +8015,0 +8016,3 +8017,3 +8018,0 +8019,0 +8020,3 +8021,0 +8022,0 +8023,3 +8024,0 +8025,0 +8026,2 +8027,1 +8028,3 +8029,0 +8030,2 +8031,2 +8032,2 +8033,2 +8034,0 +8035,0 +8036,3 +8037,2 +8038,0 +8039,3 +8040,3 +8041,0 +8042,3 +8043,0 +8044,2 +8045,3 +8046,0 +8047,0 +8048,0 +8049,2 +8050,0 +8051,3 +8052,2 +8053,0 +8054,0 +8055,1 +8056,2 +8057,3 +8058,0 +8059,1 +8060,1 +8061,1 +8062,0 +8063,0 +8064,3 +8065,2 +8066,0 +8067,0 +8068,1 +8069,0 +8070,3 +8071,0 +8072,1 +8073,1 +8074,2 +8075,2 +8076,0 +8077,0 +8078,0 +8079,0 +8080,0 +8081,0 +8082,1 +8083,3 +8084,3 +8085,0 +8086,3 +8087,0 +8088,0 +8089,0 +8090,0 +8091,0 +8092,0 +8093,0 +8094,0 +8095,0 +8096,2 +8097,2 +8098,0 +8099,3 +8100,3 +8101,2 +8102,2 +8103,0 +8104,0 +8105,3 +8106,1 +8107,0 +8108,3 +8109,2 +8110,1 +8111,0 +8112,0 +8113,3 +8114,2 +8115,1 +8116,1 +8117,0 +8118,2 +8119,0 +8120,0 +8121,0 +8122,2 +8123,2 +8124,0 +8125,3 +8126,0 +8127,1 +8128,0 +8129,0 +8130,1 +8131,0 +8132,3 +8133,1 +8134,1 +8135,1 +8136,2 +8137,2 +8138,1 +8139,1 +8140,0 +8141,2 +8142,2 +8143,0 +8144,0 +8145,1 +8146,1 +8147,2 +8148,3 +8149,3 +8150,3 +8151,3 +8152,1 +8153,0 +8154,2 +8155,2 +8156,0 +8157,0 +8158,0 +8159,2 +8160,0 +8161,2 +8162,2 +8163,2 +8164,3 +8165,3 +8166,3 +8167,3 +8168,0 +8169,3 +8170,0 +8171,1 +8172,1 +8173,0 +8174,3 +8175,1 +8176,0 +8177,3 +8178,0 +8179,3 +8180,1 +8181,0 +8182,1 +8183,3 +8184,3 +8185,2 +8186,2 +8187,2 +8188,3 +8189,2 +8190,3 +8191,0 +8192,0 +8193,0 +8194,1 +8195,1 +8196,1 +8197,0 +8198,2 +8199,3 +8200,3 +8201,0 +8202,3 +8203,3 +8204,1 +8205,0 +8206,2 +8207,0 +8208,3 +8209,0 +8210,0 +8211,1 +8212,3 +8213,0 +8214,0 +8215,0 +8216,0 +8217,2 +8218,1 +8219,0 +8220,0 +8221,3 +8222,2 +8223,0 +8224,0 +8225,0 +8226,1 +8227,0 +8228,0 +8229,0 +8230,0 +8231,0 +8232,3 +8233,0 +8234,2 +8235,3 +8236,3 +8237,0 +8238,2 +8239,0 +8240,0 +8241,0 +8242,0 +8243,0 +8244,0 +8245,0 +8246,3 +8247,0 +8248,2 +8249,3 +8250,0 +8251,0 +8252,1 +8253,2 +8254,0 +8255,3 +8256,0 +8257,0 +8258,0 +8259,3 +8260,3 +8261,3 +8262,1 +8263,0 +8264,0 +8265,3 +8266,3 +8267,0 +8268,2 +8269,3 +8270,2 +8271,0 +8272,0 +8273,3 +8274,0 +8275,3 +8276,3 +8277,0 +8278,2 +8279,0 +8280,2 +8281,0 +8282,0 +8283,3 +8284,2 +8285,0 +8286,0 +8287,2 +8288,2 +8289,0 +8290,0 +8291,0 +8292,0 +8293,1 +8294,0 +8295,0 +8296,1 +8297,1 +8298,0 +8299,1 +8300,3 +8301,0 +8302,0 +8303,1 +8304,0 +8305,0 +8306,2 +8307,2 +8308,3 +8309,0 +8310,3 +8311,3 +8312,0 +8313,0 +8314,0 +8315,3 +8316,0 +8317,2 +8318,3 +8319,0 +8320,0 +8321,0 +8322,0 +8323,0 +8324,2 +8325,2 +8326,0 +8327,3 +8328,2 +8329,0 +8330,2 +8331,3 +8332,3 +8333,1 +8334,0 +8335,0 +8336,3 +8337,2 +8338,0 +8339,3 +8340,0 +8341,1 +8342,2 +8343,0 +8344,1 +8345,0 +8346,0 +8347,0 +8348,3 +8349,2 +8350,3 +8351,3 +8352,3 +8353,2 +8354,0 +8355,0 +8356,2 +8357,3 +8358,0 +8359,3 +8360,0 +8361,3 +8362,3 +8363,2 +8364,3 +8365,0 +8366,2 +8367,2 +8368,2 +8369,1 +8370,3 +8371,0 +8372,3 +8373,1 +8374,3 +8375,3 +8376,0 +8377,2 +8378,2 +8379,0 +8380,2 +8381,3 +8382,0 +8383,0 +8384,3 +8385,0 +8386,2 +8387,3 +8388,1 +8389,2 +8390,2 +8391,1 +8392,3 +8393,0 +8394,0 +8395,2 +8396,3 +8397,0 +8398,0 +8399,2 +8400,0 +8401,1 +8402,3 +8403,3 +8404,3 +8405,0 +8406,2 +8407,0 +8408,0 +8409,0 +8410,0 +8411,3 +8412,2 +8413,1 +8414,3 +8415,2 +8416,0 +8417,0 +8418,3 +8419,0 +8420,3 +8421,2 +8422,2 +8423,2 +8424,3 +8425,0 +8426,0 +8427,0 +8428,0 +8429,2 +8430,3 +8431,1 +8432,2 +8433,0 +8434,0 +8435,3 +8436,1 +8437,0 +8438,0 +8439,3 +8440,0 +8441,0 +8442,2 +8443,2 +8444,1 +8445,2 +8446,1 +8447,0 +8448,2 +8449,0 +8450,0 +8451,3 +8452,0 +8453,2 +8454,1 +8455,0 +8456,1 +8457,0 +8458,0 +8459,0 +8460,0 +8461,2 +8462,0 +8463,3 +8464,1 +8465,0 +8466,2 +8467,0 +8468,3 +8469,2 +8470,0 +8471,1 +8472,0 +8473,1 +8474,3 +8475,0 +8476,0 +8477,3 +8478,2 +8479,2 +8480,0 +8481,0 +8482,3 +8483,2 +8484,2 +8485,3 +8486,2 +8487,2 +8488,1 +8489,0 +8490,2 +8491,1 +8492,2 +8493,2 +8494,0 +8495,0 +8496,0 +8497,2 +8498,0 +8499,0 +8500,3 +8501,2 +8502,2 +8503,2 +8504,1 +8505,2 +8506,0 +8507,0 +8508,1 +8509,3 +8510,0 +8511,0 +8512,3 +8513,0 +8514,0 +8515,0 +8516,3 +8517,0 +8518,3 +8519,3 +8520,3 +8521,0 +8522,0 +8523,1 +8524,2 +8525,1 +8526,0 +8527,2 +8528,1 +8529,0 +8530,0 +8531,2 +8532,1 +8533,3 +8534,3 +8535,2 +8536,2 +8537,0 +8538,2 +8539,2 +8540,0 +8541,2 +8542,3 +8543,2 +8544,0 +8545,0 +8546,0 +8547,2 +8548,0 +8549,3 +8550,0 +8551,0 +8552,3 +8553,2 +8554,0 +8555,1 +8556,2 +8557,0 +8558,0 +8559,3 +8560,3 +8561,1 +8562,0 +8563,0 +8564,2 +8565,0 +8566,0 +8567,0 +8568,3 +8569,3 +8570,3 +8571,2 +8572,3 +8573,0 +8574,1 +8575,0 +8576,2 +8577,0 +8578,0 +8579,1 +8580,3 +8581,2 +8582,3 +8583,3 +8584,0 +8585,3 +8586,0 +8587,0 +8588,0 +8589,2 +8590,2 +8591,2 +8592,1 +8593,3 +8594,2 +8595,1 +8596,0 +8597,0 +8598,2 +8599,3 +8600,0 +8601,3 +8602,0 +8603,3 +8604,3 +8605,1 +8606,0 +8607,0 +8608,1 +8609,2 +8610,1 +8611,0 +8612,0 +8613,0 +8614,0 +8615,3 +8616,2 +8617,0 +8618,0 +8619,1 +8620,0 +8621,3 +8622,0 +8623,2 +8624,0 +8625,3 +8626,0 +8627,0 +8628,3 +8629,0 +8630,0 +8631,0 +8632,0 +8633,1 +8634,3 +8635,2 +8636,0 +8637,3 +8638,0 +8639,0 +8640,0 +8641,0 +8642,3 +8643,2 +8644,3 +8645,1 +8646,1 +8647,0 +8648,1 +8649,0 +8650,2 +8651,3 +8652,1 +8653,0 +8654,0 +8655,0 +8656,2 +8657,3 +8658,2 +8659,3 +8660,0 +8661,3 +8662,2 +8663,0 +8664,0 +8665,1 +8666,0 +8667,1 +8668,0 +8669,0 +8670,2 +8671,0 +8672,3 +8673,0 +8674,0 +8675,0 +8676,0 +8677,1 +8678,3 +8679,1 +8680,0 +8681,0 +8682,0 +8683,3 +8684,0 +8685,0 +8686,2 +8687,0 +8688,0 +8689,0 +8690,0 +8691,0 +8692,2 +8693,0 +8694,0 +8695,1 +8696,0 +8697,2 +8698,0 +8699,2 +8700,0 +8701,0 +8702,2 +8703,1 +8704,2 +8705,0 +8706,0 +8707,0 +8708,0 +8709,0 +8710,0 +8711,0 +8712,2 +8713,0 +8714,1 +8715,0 +8716,0 +8717,3 +8718,0 +8719,0 +8720,3 +8721,2 +8722,2 +8723,0 +8724,3 +8725,0 +8726,0 +8727,0 +8728,0 +8729,2 +8730,1 +8731,0 +8732,0 +8733,0 +8734,0 +8735,0 +8736,0 +8737,0 +8738,0 +8739,3 +8740,0 +8741,0 +8742,0 +8743,0 +8744,3 +8745,0 +8746,1 +8747,0 +8748,1 +8749,0 +8750,2 +8751,2 +8752,0 +8753,0 +8754,0 +8755,0 +8756,3 +8757,3 +8758,3 +8759,1 +8760,0 +8761,0 +8762,2 +8763,1 +8764,3 +8765,0 +8766,0 +8767,2 +8768,3 +8769,3 +8770,0 +8771,0 +8772,3 +8773,3 +8774,3 +8775,0 +8776,3 +8777,2 +8778,1 +8779,3 +8780,2 +8781,0 +8782,0 +8783,0 +8784,2 +8785,0 +8786,0 +8787,1 +8788,3 +8789,0 +8790,3 +8791,3 +8792,2 +8793,0 +8794,0 +8795,3 +8796,0 +8797,0 +8798,0 +8799,2 +8800,2 +8801,3 +8802,3 +8803,0 +8804,0 +8805,3 +8806,0 +8807,0 +8808,0 +8809,0 +8810,1 +8811,0 +8812,3 +8813,2 +8814,2 +8815,0 +8816,0 +8817,0 +8818,3 +8819,2 +8820,2 +8821,0 +8822,0 +8823,0 +8824,0 +8825,3 +8826,3 +8827,2 +8828,3 +8829,2 +8830,0 +8831,2 +8832,2 +8833,0 +8834,1 +8835,0 +8836,2 +8837,0 +8838,2 +8839,0 +8840,0 +8841,3 +8842,0 +8843,3 +8844,2 +8845,0 +8846,3 +8847,3 +8848,3 +8849,3 +8850,2 +8851,0 +8852,2 +8853,3 +8854,3 +8855,0 +8856,0 +8857,0 +8858,0 +8859,0 +8860,3 +8861,3 +8862,3 +8863,2 +8864,0 +8865,0 +8866,3 +8867,1 +8868,3 +8869,0 +8870,0 +8871,0 +8872,2 +8873,0 +8874,0 +8875,3 +8876,1 +8877,3 +8878,1 +8879,0 +8880,2 +8881,0 +8882,2 +8883,3 +8884,0 +8885,1 +8886,0 +8887,0 +8888,0 +8889,0 +8890,3 +8891,0 +8892,0 +8893,0 +8894,1 +8895,1 +8896,2 +8897,0 +8898,3 +8899,1 +8900,3 +8901,0 +8902,2 +8903,1 +8904,2 +8905,0 +8906,0 +8907,0 +8908,0 +8909,0 +8910,3 +8911,0 +8912,2 +8913,3 +8914,2 +8915,3 +8916,2 +8917,0 +8918,2 +8919,0 +8920,0 +8921,1 +8922,3 +8923,2 +8924,0 +8925,3 +8926,0 +8927,3 +8928,1 +8929,1 +8930,1 +8931,0 +8932,0 +8933,2 +8934,3 +8935,2 +8936,1 +8937,2 +8938,3 +8939,2 +8940,1 +8941,2 +8942,3 +8943,2 +8944,2 +8945,0 +8946,1 +8947,1 +8948,2 +8949,3 +8950,0 +8951,0 +8952,3 +8953,2 +8954,1 +8955,0 +8956,3 +8957,0 +8958,3 +8959,0 +8960,0 +8961,2 +8962,3 +8963,0 +8964,2 +8965,1 +8966,0 +8967,0 +8968,1 +8969,2 +8970,2 +8971,2 +8972,3 +8973,3 +8974,3 +8975,0 +8976,1 +8977,0 +8978,3 +8979,0 +8980,0 +8981,3 +8982,1 +8983,0 +8984,1 +8985,0 +8986,0 +8987,0 +8988,2 +8989,0 +8990,3 +8991,0 +8992,0 +8993,3 +8994,0 +8995,0 +8996,3 +8997,1 +8998,0 +8999,0 +9000,2 +9001,1 +9002,3 +9003,0 +9004,0 +9005,0 +9006,1 +9007,2 +9008,0 +9009,0 +9010,3 +9011,3 +9012,0 +9013,0 +9014,0 +9015,3 +9016,3 +9017,0 +9018,2 +9019,3 +9020,0 +9021,2 +9022,2 +9023,2 +9024,2 +9025,3 +9026,2 +9027,1 +9028,2 +9029,2 +9030,0 +9031,1 +9032,0 +9033,0 +9034,3 +9035,0 +9036,3 +9037,2 +9038,1 +9039,0 +9040,0 +9041,0 +9042,0 +9043,0 +9044,0 +9045,2 +9046,0 +9047,1 +9048,0 +9049,3 +9050,2 +9051,2 +9052,0 +9053,0 +9054,1 +9055,3 +9056,0 +9057,2 +9058,3 +9059,2 +9060,0 +9061,2 +9062,1 +9063,3 +9064,0 +9065,0 +9066,0 +9067,3 +9068,0 +9069,0 +9070,0 +9071,0 +9072,0 +9073,1 +9074,0 +9075,1 +9076,0 +9077,3 +9078,0 +9079,0 +9080,3 +9081,0 +9082,0 +9083,0 +9084,1 +9085,0 +9086,0 +9087,1 +9088,1 +9089,2 +9090,3 +9091,0 +9092,0 +9093,0 +9094,3 +9095,0 +9096,3 +9097,0 +9098,1 +9099,2 +9100,0 +9101,3 +9102,2 +9103,0 +9104,0 +9105,1 +9106,0 +9107,0 +9108,0 +9109,2 +9110,0 +9111,0 +9112,0 +9113,3 +9114,3 +9115,3 +9116,0 +9117,1 +9118,0 +9119,1 +9120,0 +9121,2 +9122,1 +9123,1 +9124,3 +9125,1 +9126,2 +9127,1 +9128,3 +9129,1 +9130,0 +9131,0 +9132,0 +9133,3 +9134,0 +9135,1 +9136,0 +9137,2 +9138,3 +9139,0 +9140,3 +9141,2 +9142,0 +9143,3 +9144,0 +9145,0 +9146,2 +9147,2 +9148,2 +9149,0 +9150,1 +9151,0 +9152,0 +9153,3 +9154,3 +9155,2 +9156,2 +9157,3 +9158,2 +9159,3 +9160,0 +9161,3 +9162,3 +9163,3 +9164,1 +9165,1 +9166,0 +9167,0 +9168,2 +9169,0 +9170,0 +9171,1 +9172,2 +9173,3 +9174,1 +9175,0 +9176,2 +9177,0 +9178,1 +9179,0 +9180,0 +9181,1 +9182,3 +9183,2 +9184,0 +9185,1 +9186,3 +9187,3 +9188,0 +9189,3 +9190,0 +9191,0 +9192,0 +9193,0 +9194,0 +9195,0 +9196,2 +9197,1 +9198,1 +9199,2 +9200,3 +9201,0 +9202,0 +9203,3 +9204,1 +9205,0 +9206,3 +9207,3 +9208,0 +9209,0 +9210,0 +9211,0 +9212,3 +9213,0 +9214,2 +9215,2 +9216,0 +9217,0 +9218,0 +9219,2 +9220,2 +9221,0 +9222,1 +9223,2 +9224,0 +9225,0 +9226,0 +9227,0 +9228,0 +9229,1 +9230,3 +9231,0 +9232,0 +9233,2 +9234,0 +9235,1 +9236,2 +9237,1 +9238,3 +9239,0 +9240,3 +9241,2 +9242,3 +9243,1 +9244,3 +9245,1 +9246,0 +9247,3 +9248,3 +9249,1 +9250,0 +9251,0 +9252,2 +9253,3 +9254,0 +9255,2 +9256,1 +9257,0 +9258,1 +9259,2 +9260,2 +9261,0 +9262,0 +9263,2 +9264,1 +9265,0 +9266,3 +9267,3 +9268,0 +9269,0 +9270,3 +9271,1 +9272,0 +9273,1 +9274,2 +9275,0 +9276,3 +9277,0 +9278,1 +9279,0 +9280,0 +9281,1 +9282,1 +9283,1 +9284,0 +9285,0 +9286,2 +9287,0 +9288,2 +9289,2 +9290,2 +9291,1 +9292,3 +9293,0 +9294,3 +9295,2 +9296,0 +9297,3 +9298,0 +9299,2 +9300,2 +9301,0 +9302,0 +9303,3 +9304,1 +9305,0 +9306,1 +9307,2 +9308,3 +9309,3 +9310,1 +9311,3 +9312,3 +9313,2 +9314,3 +9315,0 +9316,2 +9317,3 +9318,0 +9319,0 +9320,1 +9321,2 +9322,2 +9323,2 +9324,0 +9325,0 +9326,3 +9327,2 +9328,0 +9329,0 +9330,2 +9331,2 +9332,3 +9333,3 +9334,0 +9335,2 +9336,3 +9337,2 +9338,0 +9339,0 +9340,3 +9341,0 +9342,0 +9343,2 +9344,0 +9345,3 +9346,0 +9347,0 +9348,3 +9349,3 +9350,2 +9351,3 +9352,2 +9353,0 +9354,2 +9355,1 +9356,2 +9357,0 +9358,0 +9359,3 +9360,3 +9361,0 +9362,0 +9363,1 +9364,3 +9365,0 +9366,0 +9367,0 +9368,0 +9369,1 +9370,2 +9371,2 +9372,3 +9373,3 +9374,3 +9375,1 +9376,0 +9377,2 +9378,2 +9379,0 +9380,3 +9381,1 +9382,0 +9383,0 +9384,0 +9385,0 +9386,3 +9387,2 +9388,3 +9389,3 +9390,0 +9391,0 +9392,0 +9393,3 +9394,2 +9395,0 +9396,0 +9397,1 +9398,1 +9399,0 +9400,2 +9401,0 +9402,3 +9403,2 +9404,0 +9405,0 +9406,0 +9407,3 +9408,0 +9409,3 +9410,3 +9411,1 +9412,1 +9413,0 +9414,0 +9415,0 +9416,2 +9417,2 +9418,0 +9419,2 +9420,0 +9421,0 +9422,2 +9423,2 +9424,3 +9425,0 +9426,0 +9427,2 +9428,0 +9429,0 +9430,0 +9431,2 +9432,2 +9433,0 +9434,3 +9435,0 +9436,0 +9437,2 +9438,0 +9439,3 +9440,0 +9441,0 +9442,0 +9443,0 +9444,0 +9445,1 +9446,0 +9447,1 +9448,1 +9449,0 +9450,0 +9451,0 +9452,0 +9453,1 +9454,0 +9455,0 +9456,0 +9457,3 +9458,2 +9459,0 +9460,0 +9461,0 +9462,0 +9463,0 +9464,3 +9465,0 +9466,2 +9467,1 +9468,2 +9469,2 +9470,2 +9471,3 +9472,0 +9473,3 +9474,2 +9475,0 +9476,0 +9477,0 +9478,1 +9479,2 +9480,0 +9481,3 +9482,3 +9483,2 +9484,1 +9485,3 +9486,3 +9487,3 +9488,2 +9489,0 +9490,3 +9491,1 +9492,0 +9493,0 +9494,1 +9495,0 +9496,1 +9497,2 +9498,2 +9499,0 +9500,0 +9501,1 +9502,2 +9503,0 +9504,0 +9505,0 +9506,0 +9507,1 +9508,0 +9509,2 +9510,0 +9511,3 +9512,2 +9513,2 +9514,3 +9515,2 +9516,2 +9517,0 +9518,0 +9519,1 +9520,3 +9521,0 +9522,1 +9523,1 +9524,0 +9525,0 +9526,2 +9527,0 +9528,0 +9529,0 +9530,3 +9531,0 +9532,2 +9533,2 +9534,1 +9535,0 +9536,3 +9537,3 +9538,0 +9539,0 +9540,0 +9541,3 +9542,0 +9543,1 +9544,2 +9545,3 +9546,0 +9547,1 +9548,3 +9549,3 +9550,2 +9551,2 +9552,0 +9553,0 +9554,1 +9555,3 +9556,3 +9557,0 +9558,1 +9559,3 +9560,3 +9561,1 +9562,2 +9563,3 +9564,2 +9565,0 +9566,2 +9567,3 +9568,0 +9569,0 +9570,1 +9571,2 +9572,1 +9573,0 +9574,0 +9575,0 +9576,0 +9577,0 +9578,2 +9579,1 +9580,0 +9581,1 +9582,0 +9583,3 +9584,3 +9585,2 +9586,0 +9587,0 +9588,0 +9589,3 +9590,0 +9591,3 +9592,2 +9593,1 +9594,3 +9595,3 +9596,0 +9597,3 +9598,0 +9599,1 +9600,0 +9601,0 +9602,1 +9603,1 +9604,0 +9605,2 +9606,0 +9607,3 +9608,3 +9609,2 +9610,2 +9611,0 +9612,0 +9613,0 +9614,1 +9615,0 +9616,2 +9617,0 +9618,0 +9619,1 +9620,2 +9621,0 +9622,0 +9623,0 +9624,2 +9625,2 +9626,1 +9627,0 +9628,3 +9629,3 +9630,1 +9631,1 +9632,0 +9633,3 +9634,0 +9635,0 +9636,0 +9637,0 +9638,2 +9639,0 +9640,3 +9641,0 +9642,2 +9643,0 +9644,0 +9645,0 +9646,1 +9647,3 +9648,2 +9649,0 +9650,1 +9651,3 +9652,2 +9653,3 +9654,1 +9655,3 +9656,0 +9657,2 +9658,2 +9659,0 +9660,0 +9661,0 +9662,0 +9663,0 +9664,0 +9665,1 +9666,2 +9667,3 +9668,3 +9669,0 +9670,2 +9671,2 +9672,0 +9673,0 +9674,3 +9675,0 +9676,2 +9677,0 +9678,3 +9679,0 +9680,1 +9681,0 +9682,3 +9683,2 +9684,2 +9685,2 +9686,2 +9687,0 +9688,0 +9689,0 +9690,3 +9691,1 +9692,3 +9693,0 +9694,0 +9695,0 +9696,3 +9697,0 +9698,3 +9699,2 +9700,2 +9701,0 +9702,0 +9703,2 +9704,1 +9705,3 +9706,2 +9707,0 +9708,3 +9709,0 +9710,1 +9711,3 +9712,3 +9713,3 +9714,3 +9715,0 +9716,0 +9717,2 +9718,0 +9719,3 +9720,2 +9721,0 +9722,3 +9723,3 +9724,0 +9725,0 +9726,0 +9727,0 +9728,3 +9729,0 +9730,1 +9731,0 +9732,3 +9733,0 +9734,0 +9735,1 +9736,1 +9737,3 +9738,0 +9739,2 +9740,3 +9741,3 +9742,1 +9743,2 +9744,2 +9745,0 +9746,0 +9747,3 +9748,1 +9749,0 +9750,2 +9751,0 +9752,0 +9753,0 +9754,1 +9755,2 +9756,3 +9757,3 +9758,3 +9759,0 +9760,0 +9761,3 +9762,0 +9763,3 +9764,0 +9765,1 +9766,3 +9767,2 +9768,2 +9769,0 +9770,3 +9771,1 +9772,2 +9773,2 +9774,0 +9775,0 +9776,3 +9777,3 +9778,1 +9779,0 +9780,1 +9781,0 +9782,0 +9783,0 +9784,0 +9785,0 +9786,0 +9787,0 +9788,0 +9789,0 +9790,1 +9791,0 +9792,3 +9793,3 +9794,1 +9795,2 +9796,0 +9797,0 +9798,2 +9799,0 +9800,0 +9801,1 +9802,0 +9803,3 +9804,0 +9805,3 +9806,0 +9807,0 +9808,0 +9809,0 +9810,3 +9811,3 +9812,1 +9813,2 +9814,2 +9815,3 +9816,0 +9817,1 +9818,2 +9819,2 +9820,1 +9821,0 +9822,0 +9823,2 +9824,0 +9825,0 +9826,2 +9827,2 +9828,0 +9829,0 +9830,0 +9831,0 +9832,0 +9833,0 +9834,3 +9835,0 +9836,0 +9837,2 +9838,0 +9839,3 +9840,0 +9841,1 +9842,1 +9843,3 +9844,2 +9845,3 +9846,0 +9847,0 +9848,0 +9849,2 +9850,0 +9851,1 +9852,0 +9853,3 +9854,3 +9855,0 +9856,0 +9857,0 +9858,0 +9859,3 +9860,2 +9861,1 +9862,0 +9863,2 +9864,2 +9865,0 +9866,0 +9867,0 +9868,0 +9869,0 +9870,0 +9871,2 +9872,0 +9873,2 +9874,0 +9875,0 +9876,0 +9877,0 +9878,1 +9879,2 +9880,1 +9881,2 +9882,3 +9883,3 +9884,1 +9885,1 +9886,0 +9887,2 +9888,2 +9889,3 +9890,3 +9891,0 +9892,0 +9893,2 +9894,3 +9895,0 +9896,1 +9897,0 +9898,3 +9899,0 +9900,0 +9901,0 +9902,0 +9903,3 +9904,0 +9905,2 +9906,0 +9907,2 +9908,2 +9909,0 +9910,0 +9911,0 +9912,0 +9913,2 +9914,0 +9915,3 +9916,0 +9917,3 +9918,1 +9919,0 +9920,2 +9921,3 +9922,1 +9923,3 +9924,2 +9925,3 +9926,3 +9927,0 +9928,0 +9929,3 +9930,1 +9931,0 +9932,2 +9933,3 +9934,0 +9935,1 +9936,0 +9937,0 +9938,0 +9939,0 +9940,3 +9941,1 +9942,0 +9943,3 +9944,3 +9945,3 +9946,0 +9947,0 +9948,0 +9949,1 +9950,1 +9951,1 +9952,3 +9953,3 +9954,1 +9955,1 +9956,2 +9957,1 +9958,2 +9959,0 +9960,0 +9961,0 +9962,0 +9963,2 +9964,2 +9965,0 +9966,2 +9967,0 +9968,0 +9969,3 +9970,0 +9971,1 +9972,1 +9973,3 +9974,0 +9975,0 +9976,1 +9977,1 +9978,3 +9979,0 +9980,3 +9981,0 +9982,0 +9983,0 +9984,2 +9985,3 +9986,3 +9987,2 +9988,1 +9989,2 +9990,1 +9991,1 +9992,1 +9993,3 +9994,2 +9995,3 +9996,0 +9997,0 +9998,0 +9999,0 +10000,0 +10001,0 +10002,0 +10003,0 +10004,0 +10005,2 +10006,3 +10007,0 +10008,0 +10009,2 +10010,3 +10011,3 +10012,1 +10013,0 +10014,0 +10015,2 +10016,3 +10017,0 +10018,3 +10019,2 +10020,3 +10021,0 +10022,3 +10023,1 +10024,1 +10025,0 +10026,1 +10027,0 +10028,2 +10029,0 +10030,0 +10031,3 +10032,3 +10033,2 +10034,3 +10035,2 +10036,1 +10037,0 +10038,0 +10039,0 +10040,0 +10041,2 +10042,2 +10043,3 +10044,2 +10045,3 +10046,3 +10047,1 +10048,0 +10049,2 +10050,0 +10051,1 +10052,0 +10053,3 +10054,0 +10055,1 +10056,0 +10057,3 +10058,2 +10059,0 +10060,1 +10061,2 +10062,3 +10063,3 +10064,1 +10065,2 +10066,1 +10067,2 +10068,1 +10069,0 +10070,1 +10071,0 +10072,0 +10073,0 +10074,3 +10075,0 +10076,0 +10077,3 +10078,3 +10079,3 +10080,2 +10081,1 +10082,2 +10083,0 +10084,1 +10085,3 +10086,0 +10087,1 +10088,1 +10089,2 +10090,1 +10091,2 +10092,0 +10093,2 +10094,2 +10095,0 +10096,2 +10097,3 +10098,2 +10099,3 +10100,3 +10101,3 +10102,1 +10103,0 +10104,3 +10105,0 +10106,0 +10107,1 +10108,0 +10109,3 +10110,2 +10111,0 +10112,0 +10113,2 +10114,0 +10115,2 +10116,2 +10117,2 +10118,0 +10119,2 +10120,2 +10121,1 +10122,0 +10123,0 +10124,3 +10125,3 +10126,3 +10127,3 +10128,0 +10129,0 +10130,0 +10131,3 +10132,2 +10133,0 +10134,0 +10135,1 +10136,0 +10137,0 +10138,0 +10139,0 +10140,0 +10141,0 +10142,1 +10143,0 +10144,0 +10145,2 +10146,0 +10147,0 +10148,2 +10149,0 +10150,2 +10151,0 +10152,3 +10153,2 +10154,0 +10155,0 +10156,0 +10157,3 +10158,0 +10159,3 +10160,0 +10161,3 +10162,1 +10163,0 +10164,2 +10165,0 +10166,0 +10167,0 +10168,0 +10169,0 +10170,0 +10171,0 +10172,3 +10173,3 +10174,3 +10175,2 +10176,3 +10177,1 +10178,1 +10179,1 +10180,3 +10181,0 +10182,0 +10183,0 +10184,0 +10185,2 +10186,2 +10187,3 +10188,0 +10189,3 +10190,0 +10191,0 +10192,2 +10193,3 +10194,2 +10195,0 +10196,2 +10197,3 +10198,0 +10199,2 +10200,0 +10201,2 +10202,1 +10203,2 +10204,0 +10205,1 +10206,0 +10207,0 +10208,2 +10209,0 +10210,3 +10211,0 +10212,0 +10213,0 +10214,0 +10215,0 +10216,3 +10217,0 +10218,0 +10219,1 +10220,3 +10221,3 +10222,3 +10223,0 +10224,3 +10225,0 +10226,0 +10227,0 +10228,2 +10229,2 +10230,0 +10231,0 +10232,0 +10233,1 +10234,0 +10235,3 +10236,3 +10237,0 +10238,1 +10239,3 +10240,2 +10241,2 +10242,0 +10243,2 +10244,0 +10245,3 +10246,1 +10247,0 +10248,0 +10249,2 +10250,3 +10251,2 +10252,0 +10253,3 +10254,3 +10255,2 +10256,0 +10257,3 +10258,0 +10259,0 +10260,0 +10261,0 +10262,0 +10263,0 +10264,0 +10265,2 +10266,0 +10267,0 +10268,2 +10269,0 +10270,0 +10271,0 +10272,0 +10273,0 +10274,1 +10275,1 +10276,0 +10277,3 +10278,0 +10279,0 +10280,2 +10281,3 +10282,1 +10283,1 +10284,0 +10285,2 +10286,0 +10287,3 +10288,0 +10289,3 +10290,2 +10291,0 +10292,2 +10293,3 +10294,3 +10295,2 +10296,2 +10297,0 +10298,0 +10299,0 +10300,3 +10301,3 +10302,2 +10303,0 +10304,0 +10305,2 +10306,0 +10307,3 +10308,0 +10309,3 +10310,3 +10311,0 +10312,1 +10313,0 +10314,1 +10315,0 +10316,0 +10317,2 +10318,0 +10319,1 +10320,1 +10321,0 +10322,0 +10323,0 +10324,0 +10325,2 +10326,3 +10327,3 +10328,3 +10329,3 +10330,0 +10331,0 +10332,1 +10333,1 +10334,1 +10335,0 +10336,0 +10337,2 +10338,0 +10339,0 +10340,3 +10341,2 +10342,0 +10343,2 +10344,2 +10345,0 +10346,0 +10347,2 +10348,2 +10349,0 +10350,0 +10351,0 +10352,0 +10353,0 +10354,0 +10355,2 +10356,0 +10357,0 +10358,0 +10359,0 +10360,0 +10361,1 +10362,0 +10363,2 +10364,2 +10365,1 +10366,0 +10367,0 +10368,2 +10369,0 +10370,2 +10371,0 +10372,0 +10373,3 +10374,0 +10375,2 +10376,0 +10377,3 +10378,0 +10379,3 +10380,0 +10381,2 +10382,2 +10383,0 +10384,1 +10385,0 +10386,0 +10387,3 +10388,0 +10389,1 +10390,3 +10391,0 +10392,0 +10393,0 +10394,2 +10395,0 +10396,2 +10397,0 +10398,0 +10399,0 +10400,2 +10401,0 +10402,3 +10403,0 +10404,3 +10405,0 +10406,2 +10407,3 +10408,0 +10409,0 +10410,0 +10411,1 +10412,3 +10413,0 +10414,1 +10415,0 +10416,0 +10417,2 +10418,2 +10419,1 +10420,0 +10421,1 +10422,0 +10423,0 +10424,1 +10425,2 +10426,2 +10427,0 +10428,3 +10429,0 +10430,0 +10431,0 +10432,1 +10433,0 +10434,3 +10435,0 +10436,3 +10437,3 +10438,1 +10439,1 +10440,0 +10441,0 +10442,0 +10443,0 +10444,3 +10445,2 +10446,3 +10447,2 +10448,1 +10449,3 +10450,3 +10451,0 +10452,3 +10453,0 +10454,2 +10455,0 +10456,0 +10457,3 +10458,0 +10459,3 +10460,1 +10461,0 +10462,3 +10463,0 +10464,2 +10465,1 +10466,0 +10467,3 +10468,1 +10469,0 +10470,3 +10471,0 +10472,1 +10473,0 +10474,1 +10475,0 +10476,1 +10477,0 +10478,1 +10479,1 +10480,0 +10481,0 +10482,3 +10483,1 +10484,0 +10485,1 +10486,1 +10487,0 +10488,0 +10489,0 +10490,2 +10491,3 +10492,0 +10493,0 +10494,0 +10495,0 +10496,1 +10497,0 +10498,0 +10499,1 +10500,2 +10501,2 +10502,0 +10503,0 +10504,3 +10505,3 +10506,0 +10507,1 +10508,3 +10509,0 +10510,2 +10511,3 +10512,0 +10513,2 +10514,0 +10515,2 +10516,3 +10517,0 +10518,2 +10519,1 +10520,2 +10521,0 +10522,0 +10523,2 +10524,0 +10525,0 +10526,3 +10527,2 +10528,1 +10529,1 +10530,0 +10531,2 +10532,1 +10533,3 +10534,3 +10535,1 +10536,2 +10537,0 +10538,1 +10539,1 +10540,0 +10541,2 +10542,0 +10543,1 +10544,2 +10545,3 +10546,0 +10547,2 +10548,0 +10549,1 +10550,0 +10551,3 +10552,0 +10553,0 +10554,0 +10555,2 +10556,0 +10557,3 +10558,2 +10559,0 +10560,2 +10561,0 +10562,3 +10563,1 +10564,0 +10565,0 +10566,0 +10567,3 +10568,0 +10569,0 +10570,2 +10571,0 +10572,0 +10573,0 +10574,0 +10575,0 +10576,2 +10577,0 +10578,2 +10579,0 +10580,0 +10581,2 +10582,0 +10583,2 +10584,0 +10585,3 +10586,0 +10587,0 +10588,2 +10589,2 +10590,0 +10591,3 +10592,2 +10593,0 +10594,0 +10595,0 +10596,0 +10597,0 +10598,2 +10599,2 +10600,2 +10601,1 +10602,1 +10603,2 +10604,3 +10605,3 +10606,0 +10607,3 +10608,1 +10609,0 +10610,3 +10611,0 +10612,0 +10613,3 +10614,3 +10615,2 +10616,1 +10617,1 +10618,3 +10619,3 +10620,1 +10621,0 +10622,2 +10623,0 +10624,2 +10625,2 +10626,3 +10627,1 +10628,0 +10629,0 +10630,3 +10631,0 +10632,0 +10633,3 +10634,1 +10635,3 +10636,0 +10637,0 +10638,3 +10639,1 +10640,0 +10641,0 +10642,3 +10643,3 +10644,1 +10645,0 +10646,1 +10647,1 +10648,3 +10649,2 +10650,1 +10651,2 +10652,0 +10653,2 +10654,2 +10655,1 +10656,2 +10657,1 +10658,3 +10659,1 +10660,1 +10661,1 +10662,0 +10663,1 +10664,2 +10665,0 +10666,3 +10667,0 +10668,2 +10669,0 +10670,2 +10671,0 +10672,2 +10673,1 +10674,0 +10675,1 +10676,2 +10677,0 +10678,3 +10679,2 +10680,3 +10681,1 +10682,0 +10683,0 +10684,0 +10685,3 +10686,3 +10687,2 +10688,2 +10689,0 +10690,0 +10691,1 +10692,0 +10693,3 +10694,0 +10695,2 +10696,2 +10697,0 +10698,0 +10699,0 +10700,2 +10701,3 +10702,0 +10703,2 +10704,2 +10705,1 +10706,0 +10707,3 +10708,3 +10709,0 +10710,0 +10711,2 +10712,0 +10713,0 +10714,2 +10715,1 +10716,2 +10717,0 +10718,3 +10719,3 +10720,0 +10721,0 +10722,0 +10723,2 +10724,1 +10725,3 +10726,0 +10727,2 +10728,0 +10729,2 +10730,0 +10731,2 +10732,0 +10733,3 +10734,0 +10735,1 +10736,3 +10737,2 +10738,0 +10739,0 +10740,0 +10741,0 +10742,3 +10743,3 +10744,0 diff --git a/new_model_tester.py b/new_model_tester.py new file mode 100644 index 0000000000000000000000000000000000000000..ac76d07f73f9772290853fb343c106b146e1ec83 --- /dev/null +++ b/new_model_tester.py @@ -0,0 +1,67 @@ +from features_extractor import FeatureExtractor + +import pandas as pd +import numpy as np +import xgboost as xgb +import pickle +import time + +from sklearn.model_selection import cross_validate, cross_val_predict, train_test_split, GridSearchCV +from sklearn.metrics import accuracy_score, f1_score, confusion_matrix +from sklearn.neighbors import KNeighborsClassifier +from sklearn.svm import SVC +from sklearn.ensemble import VotingClassifier, RandomForestClassifier + +from matplotlib import pyplot +from copy import copy, deepcopy + + +simple_categorical_variables = ['salutations', 'designation'] +complex_categorical_variables = ['mail_type', 'tld', 'org'] +numerical_variables = ['ccs', 'bcced', 'images', 'urls', 'chars_in_subject', 'chars_in_body'] + +## Build the features extractor +fe = FeatureExtractor(['train.csv', 'train_legacy.csv'], test_size=0.2) + +base_model = xgb.XGBClassifier(n_estimators=100, max_depth=12, gamma=0, reg_lambda=0.1, reg_alpha=0, + learning_rate=0.1, subsample=0.8, colsample_bytree=0.5, + min_child_weight=0.2, scale_pos_weight=1, + objective='multi_softprob', verbosity=1) + +X_train, Y_train, X_test, Y_test, X_true = fe.extract_features('test.csv', + simple_categorical_variables=simple_categorical_variables, + complex_categorical_variables=complex_categorical_variables, + numerical_variables=numerical_variables, + time_variable=None) + +param_grid = {'n_estimators':[300, 400, 500], 'max_depth':[12], 'learning_rate':[0.01, 0.02, 0.05], + 'gamma':[0], 'reg_lambda':[0, 0.1, 0.2], 'reg_alpha':[0]} +grid = GridSearchCV(estimator=base_model, param_grid=param_grid, scoring='f1_macro', n_jobs=8, cv=2, verbose=1) +grid.fit(X_train, Y_train) + +best_params = grid.best_params_ +print('Best params: {}'.format(best_params)) + +base_model = base_model.set_params(params=best_params) +scores = cross_validate(base_model, X_train, Y_train, cv=8, + verbose=1, return_estimator=True, return_train_score=True, + n_jobs=8, scoring='f1_macro') + +print("Train : ", np.mean(scores['train_score']), "Valid : ",np.mean(scores['test_score'])) +print(scores['test_score']) + +best_model = scores['estimator'][np.argmax(scores['test_score'])] +test_pred = best_model.predict(X_test) +print("Best Test : ", f1_score(Y_test, test_pred, average=None)) +print(confusion_matrix(Y_test, test_pred)) + +voting_model = VotingClassifier(estimators=scores['estimator'], voting='soft') +voting_model.fit(X_train, Y_train) +y_test_pred_median = voting_model.predict(X_test) + +y_test_pred_median = np.argmax(y_test_pred_median, axis=1) +test_median_score = f1_score(Y_test, y_test_pred_median, average=None) +print("Median Test : ", test_median_score) +print(confusion_matrix(Y_test, y_test_pred_median)) + + diff --git a/saved_stacks/0.1_knn_classifier_rd_0.9802165556813452.dat b/saved_stacks/0.1_knn_classifier_rd_0.9802165556813452.dat new file mode 100644 index 0000000000000000000000000000000000000000..34c346dc26d6605cc50d4e0fa26e0d086325fecf Binary files /dev/null and b/saved_stacks/0.1_knn_classifier_rd_0.9802165556813452.dat differ diff --git a/saved_stacks/0.1_knn_classifier_tuned_0.9788529160892514.dat b/saved_stacks/0.1_knn_classifier_tuned_0.9788529160892514.dat new file mode 100644 index 0000000000000000000000000000000000000000..786d53e75bd4589279984da9a3a700cc8a9000e6 Binary files /dev/null and b/saved_stacks/0.1_knn_classifier_tuned_0.9788529160892514.dat differ diff --git a/saved_stacks/0.1_xgb_multi_softprob_rd_0.9806327380947029.dat b/saved_stacks/0.1_xgb_multi_softprob_rd_0.9806327380947029.dat new file mode 100644 index 0000000000000000000000000000000000000000..2ed76748c69aa48aab4d381a2014993b664347b9 Binary files /dev/null and b/saved_stacks/0.1_xgb_multi_softprob_rd_0.9806327380947029.dat differ diff --git a/saved_stacks/0.1_xgb_multi_softprob_tuned_0.9803752131450765.dat b/saved_stacks/0.1_xgb_multi_softprob_tuned_0.9803752131450765.dat new file mode 100644 index 0000000000000000000000000000000000000000..36d90015dac38b4552e180cc635ad6f1ace746af Binary files /dev/null and b/saved_stacks/0.1_xgb_multi_softprob_tuned_0.9803752131450765.dat differ diff --git a/skeleton_code.py b/skeleton_code.py deleted file mode 100644 index bdd7cb30deadfb94dedcdd72e6ddcad5b2bd1aa7..0000000000000000000000000000000000000000 --- a/skeleton_code.py +++ /dev/null @@ -1,153 +0,0 @@ -""" -This script can be used as skelton code to read the challenge train and test -csvs, to train a trivial model, and write data to the submission file. -""" -import pandas as pd -import numpy as np -import xgboost as xgb - -from datetime import datetime - -from sklearn.preprocessing import OneHotEncoder, MinMaxScaler, StandardScaler -from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA -from sklearn.neighbors import KNeighborsClassifier -from sklearn.model_selection import cross_validate, train_test_split -from sklearn.metrics import accuracy_score, f1_score - -from matplotlib import pyplot - - -def timestampToMonthDayHour(): - return - - -## Read csvs -train_df = pd.read_csv('train.csv', index_col=0) -train_df2 = pd.read_csv('train_legacy.csv', index_col=0) -train_df = pd.concat((train_df,train_df2), axis=0) -true_test_df = pd.read_csv('test.csv', index_col=0) -train_df, test_df = train_test_split(train_df, test_size=0.20, random_state=42) - -## Handle missing values -train_df.fillna('NA', inplace=True) -test_df.fillna('NA', inplace=True) -true_test_df.fillna('NA', inplace=True) - -## Filtering column "mail_type" -print(train_df) - -train_y = np.ravel(train_df[['label']]) -test_y = np.ravel(test_df[['label']]) - -sample_per_class = np.sum(OneHotEncoder(sparse=False).fit_transform(train_df[['label']].to_numpy()), axis=0) -print(sample_per_class/np.sum(sample_per_class)) - -categorical_variables = ['salutations', 'designation'] -simple_numerical_variables = ['ccs', 'bcced', 'images', 'urls', 'chars_in_subject', 'chars_in_body'] - -train_x_categorical = train_df[categorical_variables].to_numpy() -test_x_categorical = test_df[categorical_variables].to_numpy() -true_x_categorical = true_test_df[categorical_variables].to_numpy() - -train_x_numerical = train_df[simple_numerical_variables].to_numpy() -test_x_numerical = test_df[simple_numerical_variables].to_numpy() -true_x_numerical = true_test_df[simple_numerical_variables].to_numpy() - -x_train_list = [] -x_test_list = [] -x_true_list = [] -features_labels = [] - -## Do one hot encoding of categorical features -feat_enc_categorical = OneHotEncoder(handle_unknown='ignore', sparse=False) - -train_x_categorical = feat_enc_categorical.fit_transform(train_x_categorical) -test_x_categorical = feat_enc_categorical.transform(test_x_categorical) -true_x_categorical = feat_enc_categorical.transform(true_x_categorical) - -x_train_list.append(train_x_categorical) -x_test_list.append(test_x_categorical) -x_true_list.append(true_x_categorical) -features_labels += [categorical_variables[0]]*2 + [categorical_variables[1]]*2 - -## Do standard normalisation for simple numerical features* -feat_enc_numerical = StandardScaler() - -train_x_numerical = feat_enc_numerical.fit_transform(train_x_numerical) -test_x_numerical = feat_enc_numerical.transform(test_x_numerical) -true_x_numerical = feat_enc_numerical.transform(true_x_numerical) - -x_train_list.append(train_x_numerical) -x_test_list.append(test_x_numerical) -x_true_list.append(true_x_numerical) -features_labels += simple_numerical_variables - -## Do one hot encoding and LDA for domain name and corportation (org and tld) independently -complex_categorical_variables = ['mail_type', 'tld', 'org'] - -for variable in complex_categorical_variables: - train_x_var = train_df[[variable]].to_numpy() - test_x_var = test_df[[variable]].to_numpy() - true_x_var = true_test_df[[variable]].to_numpy() - - feat_enc = OneHotEncoder(handle_unknown='ignore', sparse=False) - train_x_var = feat_enc.fit_transform(train_x_var) - test_x_var = feat_enc.transform(test_x_var) - true_x_var = feat_enc.transform(true_x_var) - - lda = LDA() - train_x_var = lda.fit_transform(train_x_var, train_y) - test_x_var = lda.transform(test_x_var) - true_x_var = lda.transform(true_x_var) - - x_train_list.append(train_x_var) - x_test_list.append(test_x_var) - x_true_list.append(true_x_var) - features_labels += [variable]*test_x_var.shape[1] - - -## Concatenate all the features -print([x.shape for x in x_train_list]) -X_train = np.concatenate(x_train_list, axis=1) -Y_train = train_y -X_test = np.concatenate(x_test_list, axis=1) -X_true = np.concatenate(x_true_list, axis=1) -print(X_train.shape, Y_train.shape, X_test.shape, X_true.shape) - -# ## Train a simple KNN classifier using featurized data -# neigh = KNeighborsClassifier(n_neighbors=5) -# neigh.fit(X_train, Y_train) -# pred_y = neigh.predict(X_test) -# pred_df = pd.DataFrame(pred_y, columns=['label']) -# pred_df.to_csv("knn_sample_submission.csv", index=True, index_label='Id') - -## Train on xgb classifier - -model = xgb.XGBClassifier(n_estimators=250, max_depth=12, - learning_rate=0.1, subsample=0.8, colsample_bytree=0.5, - min_child_weight=0.2, scale_pos_weight=1, - objective='multi:softmax', verbosity=1) - -scores = cross_validate(model, X_train, Y_train, cv=8, - verbose=1, return_estimator=True, return_train_score=True, - n_jobs=8, scoring='f1_macro') - - -print("Train : ", np.mean(scores['train_score']), "Valid : ",np.mean(scores['test_score'])) -print(scores['test_score']) -best_model_index = np.argmax(scores['test_score']) -best_model = scores['estimator'][best_model_index] - -test_pred = best_model.predict(X_test) -print("Test : ", f1_score(test_y, test_pred, average='macro')) - -pred_y = best_model.predict(X_true) -pred_df = pd.DataFrame(pred_y, columns=['label']) -pred_df.to_csv("xgb_sample_submission.csv", index=True, index_label='Id') - -print(len(best_model.feature_importances_), len(features_labels), features_labels) -print(best_model.feature_importances_) -# pyplot.bar(features_labels, best_model.feature_importances_) -# pyplot.show() -# pyplot.bar(range(len(features_labels)), best_model.feature_importances_) -# pyplot.show() diff --git a/stacked_prediction.py b/stacked_prediction.py new file mode 100644 index 0000000000000000000000000000000000000000..a3d08c5f017ff6d2aa77a237e7b7f6ff5be39709 --- /dev/null +++ b/stacked_prediction.py @@ -0,0 +1,114 @@ +""" +This script can be used as skelton code to read the challenge train and test +csvs, to train a trivial model, and write data to the submission file. +""" +from features_extractor import FeatureExtractor + +import pandas as pd +import numpy as np +import xgboost as xgb +import pickle +import time + +from sklearn.model_selection import cross_validate, cross_val_predict, train_test_split +from sklearn.metrics import accuracy_score, f1_score, confusion_matrix +from sklearn.neighbors import KNeighborsClassifier +from sklearn.svm import SVC +from sklearn.ensemble import VotingClassifier, RandomForestClassifier + +from matplotlib import pyplot +from copy import copy, deepcopy + + +simple_categorical_variables = ['salutations', 'designation', 'bcced'] +complex_categorical_variables = ['mail_type', 'tld', 'org'] +numerical_variables = ['ccs', 'images', 'urls', 'chars_in_subject', 'chars_in_body'] +numerical_variables_to_clusterise = [] + +## Build the features extractor +test_size = 0.2 +fe = FeatureExtractor(['train.csv', 'train_legacy.csv'], test_size=test_size) + +# We will stack models in this list +stack = [] +stack_datas = [] + +# We build our models +model_types = [ + 'xgb_multi_softprob_tuned', + 'xgb_multi_softprob_rd', + 'knn_classifier_tuned', + 'knn_classifier_rd', + # 'svc_classifier' +] +models = [ + xgb.XGBClassifier(n_estimators=500, max_depth=12, gamma=0, reg_lambda=0.1, reg_alpha=0, + learning_rate=0.02, subsample=0.8, colsample_bytree=0.5, + min_child_weight=0.2, scale_pos_weight=1, + objective='multi_softprob', verbosity=1), + xgb.XGBClassifier(n_estimators=500, max_depth=12, gamma=0, reg_lambda=0, reg_alpha=0, + learning_rate=0.02, subsample=0.8, colsample_bytree=0.5, + min_child_weight=0.2, scale_pos_weight=1, + objective='multi_softprob', verbosity=1), + KNeighborsClassifier(), + KNeighborsClassifier(n_neighbors=8, p=1, weights='distance'), + SVC(C=100, degree=1, probability=True), + RandomForestClassifier(), +] + +nb_folds = 8 +features_labels = [] + +for model_type, base_model in zip(model_types, models): + + X_train, Y_train, X_test, Y_test, X_true = fe.extract_features('test.csv', + simple_categorical_variables=simple_categorical_variables, + complex_categorical_variables=complex_categorical_variables, + numerical_variables=numerical_variables, + numerical_variables_to_clusterise=numerical_variables_to_clusterise, + time_variable=None) + + scores = cross_validate(base_model, X_train, Y_train, cv=nb_folds, + verbose=1, return_estimator=True, return_train_score=True, + n_jobs=8, scoring='f1_macro') + + stack += deepcopy(scores['estimator']) + + print("Train : ", np.mean(scores['train_score']), "Valid : ",np.mean(scores['test_score'])) + print(scores['test_score']) + + best_model = scores['estimator'][np.argmax(scores['test_score'])] + test_pred = best_model.predict(X_test) + print("Best Test : ", f1_score(Y_test, test_pred, average=None)) + print(confusion_matrix(Y_test, test_pred)) + + # Take the median predicitons of every fold + y_train_pred_list = [] + y_test_pred_list = [] + y_true_pred_list = [] + for model in scores['estimator']: + y_train_pred_list += [model.predict_proba(X_train)[:, np.newaxis]] + y_test_pred_list += [model.predict_proba(X_test)[:, np.newaxis]] + y_true_pred_list += [model.predict_proba(X_true)[:, np.newaxis]] + y_train_pred_median = np.median(np.concatenate(y_train_pred_list, axis=1), axis=1) + y_test_pred_median = np.median(np.concatenate(y_test_pred_list, axis=1), axis=1) + y_true_pred_median = np.median(np.concatenate(y_true_pred_list, axis=1), axis=1) + + stack_datas = {'Y_train_pred': copy(y_train_pred_median), + 'Y_test_pred': copy(y_test_pred_median), + 'Y_true_pred': copy(y_true_pred_median), + 'feature_label': model_type, + 'test_size': test_size + } + + y_test_pred_median = np.argmax(y_test_pred_median, axis=1) + test_median_score = f1_score(Y_test, y_test_pred_median, average=None) + print("Median Test : ", test_median_score) + print(confusion_matrix(Y_test, y_test_pred_median)) + + if model_type == 'xgb_multi_softprob': + y_true_pred_median = np.argmax(y_true_pred_median, axis=1) + pred_df = pd.DataFrame(y_true_pred_median, columns=['label']) + pred_df.to_csv("xgb_sample_submission.csv", index=True, index_label='Id') + + pickle.dump(stack_datas, open("saved_stacks/{}_{}_{}.dat".format(test_size, model_type, np.mean(test_median_score)), "wb")) \ No newline at end of file diff --git a/stacked_sample_submission.csv b/stacked_sample_submission.csv new file mode 100644 index 0000000000000000000000000000000000000000..0e9cf7ecfeabc424cf1ccf764d020de4de9ce74c --- /dev/null +++ b/stacked_sample_submission.csv @@ -0,0 +1,10746 @@ +Id,label +0,2 +1,0 +2,0 +3,3 +4,0 +5,0 +6,0 +7,2 +8,2 +9,0 +10,2 +11,0 +12,0 +13,0 +14,3 +15,2 +16,1 +17,0 +18,1 +19,1 +20,0 +21,0 +22,1 +23,0 +24,3 +25,3 +26,1 +27,0 +28,1 +29,2 +30,0 +31,1 +32,0 +33,3 +34,0 +35,0 +36,3 +37,2 +38,3 +39,0 +40,0 +41,1 +42,0 +43,0 +44,1 +45,0 +46,3 +47,3 +48,3 +49,3 +50,2 +51,0 +52,3 +53,2 +54,0 +55,0 +56,3 +57,2 +58,1 +59,3 +60,0 +61,3 +62,2 +63,3 +64,3 +65,0 +66,3 +67,0 +68,0 +69,0 +70,1 +71,0 +72,2 +73,2 +74,3 +75,2 +76,0 +77,2 +78,2 +79,2 +80,1 +81,0 +82,0 +83,0 +84,2 +85,0 +86,2 +87,0 +88,2 +89,2 +90,3 +91,3 +92,3 +93,3 +94,0 +95,0 +96,0 +97,0 +98,2 +99,0 +100,2 +101,2 +102,2 +103,0 +104,3 +105,0 +106,2 +107,0 +108,0 +109,0 +110,1 +111,0 +112,0 +113,3 +114,1 +115,3 +116,2 +117,2 +118,3 +119,0 +120,3 +121,2 +122,0 +123,0 +124,3 +125,1 +126,3 +127,0 +128,1 +129,1 +130,0 +131,1 +132,2 +133,0 +134,1 +135,1 +136,3 +137,0 +138,0 +139,0 +140,0 +141,2 +142,0 +143,2 +144,2 +145,1 +146,2 +147,0 +148,0 +149,3 +150,2 +151,3 +152,3 +153,0 +154,2 +155,3 +156,3 +157,2 +158,0 +159,1 +160,1 +161,1 +162,2 +163,3 +164,0 +165,0 +166,0 +167,0 +168,0 +169,0 +170,0 +171,0 +172,0 +173,3 +174,0 +175,0 +176,0 +177,3 +178,3 +179,0 +180,3 +181,0 +182,3 +183,0 +184,0 +185,2 +186,0 +187,3 +188,0 +189,3 +190,2 +191,1 +192,0 +193,0 +194,3 +195,1 +196,3 +197,0 +198,3 +199,3 +200,1 +201,0 +202,0 +203,3 +204,0 +205,3 +206,2 +207,3 +208,2 +209,0 +210,0 +211,3 +212,3 +213,3 +214,3 +215,3 +216,3 +217,2 +218,3 +219,2 +220,0 +221,0 +222,2 +223,0 +224,3 +225,2 +226,1 +227,3 +228,0 +229,0 +230,3 +231,0 +232,0 +233,2 +234,2 +235,1 +236,0 +237,3 +238,0 +239,0 +240,0 +241,0 +242,2 +243,0 +244,0 +245,0 +246,2 +247,0 +248,0 +249,0 +250,0 +251,0 +252,0 +253,0 +254,0 +255,0 +256,3 +257,0 +258,0 +259,2 +260,0 +261,0 +262,2 +263,2 +264,0 +265,0 +266,2 +267,3 +268,2 +269,2 +270,0 +271,0 +272,3 +273,0 +274,0 +275,0 +276,3 +277,0 +278,3 +279,0 +280,1 +281,1 +282,0 +283,0 +284,2 +285,3 +286,3 +287,0 +288,0 +289,2 +290,2 +291,0 +292,3 +293,0 +294,2 +295,0 +296,2 +297,3 +298,0 +299,1 +300,1 +301,0 +302,3 +303,1 +304,0 +305,2 +306,0 +307,0 +308,0 +309,0 +310,0 +311,2 +312,2 +313,2 +314,1 +315,0 +316,1 +317,2 +318,0 +319,0 +320,3 +321,3 +322,3 +323,0 +324,3 +325,2 +326,2 +327,2 +328,3 +329,2 +330,0 +331,0 +332,3 +333,1 +334,0 +335,0 +336,2 +337,1 +338,1 +339,3 +340,0 +341,0 +342,1 +343,0 +344,0 +345,0 +346,3 +347,0 +348,3 +349,3 +350,0 +351,3 +352,1 +353,1 +354,1 +355,3 +356,2 +357,2 +358,0 +359,0 +360,0 +361,3 +362,0 +363,1 +364,3 +365,0 +366,3 +367,1 +368,0 +369,3 +370,3 +371,0 +372,2 +373,0 +374,1 +375,3 +376,0 +377,3 +378,0 +379,3 +380,0 +381,0 +382,1 +383,0 +384,3 +385,1 +386,2 +387,1 +388,1 +389,0 +390,2 +391,3 +392,2 +393,3 +394,0 +395,2 +396,0 +397,0 +398,0 +399,3 +400,0 +401,2 +402,1 +403,1 +404,0 +405,3 +406,0 +407,0 +408,0 +409,1 +410,2 +411,0 +412,3 +413,0 +414,3 +415,3 +416,3 +417,0 +418,3 +419,0 +420,0 +421,2 +422,2 +423,1 +424,3 +425,0 +426,1 +427,2 +428,2 +429,3 +430,3 +431,2 +432,2 +433,1 +434,1 +435,2 +436,0 +437,2 +438,3 +439,0 +440,3 +441,2 +442,2 +443,2 +444,0 +445,2 +446,2 +447,3 +448,0 +449,0 +450,1 +451,2 +452,3 +453,0 +454,1 +455,1 +456,0 +457,0 +458,1 +459,0 +460,0 +461,0 +462,3 +463,0 +464,3 +465,2 +466,0 +467,3 +468,0 +469,0 +470,2 +471,2 +472,0 +473,0 +474,1 +475,1 +476,0 +477,3 +478,2 +479,2 +480,0 +481,0 +482,3 +483,2 +484,3 +485,3 +486,0 +487,0 +488,3 +489,1 +490,0 +491,0 +492,3 +493,3 +494,0 +495,3 +496,0 +497,2 +498,2 +499,0 +500,3 +501,0 +502,3 +503,2 +504,3 +505,0 +506,0 +507,2 +508,0 +509,0 +510,0 +511,0 +512,3 +513,0 +514,3 +515,0 +516,0 +517,3 +518,3 +519,0 +520,0 +521,0 +522,0 +523,3 +524,2 +525,0 +526,3 +527,0 +528,0 +529,0 +530,0 +531,1 +532,0 +533,0 +534,0 +535,3 +536,2 +537,3 +538,1 +539,0 +540,1 +541,3 +542,3 +543,3 +544,0 +545,2 +546,0 +547,3 +548,0 +549,3 +550,0 +551,0 +552,3 +553,0 +554,0 +555,0 +556,1 +557,0 +558,1 +559,0 +560,0 +561,2 +562,3 +563,0 +564,0 +565,0 +566,2 +567,2 +568,0 +569,3 +570,1 +571,0 +572,3 +573,0 +574,0 +575,0 +576,0 +577,3 +578,3 +579,0 +580,0 +581,0 +582,0 +583,0 +584,0 +585,3 +586,0 +587,0 +588,0 +589,1 +590,2 +591,0 +592,0 +593,2 +594,0 +595,3 +596,0 +597,1 +598,0 +599,3 +600,3 +601,0 +602,0 +603,0 +604,2 +605,0 +606,1 +607,1 +608,1 +609,1 +610,2 +611,0 +612,2 +613,0 +614,1 +615,2 +616,2 +617,0 +618,2 +619,0 +620,0 +621,2 +622,2 +623,1 +624,0 +625,0 +626,2 +627,0 +628,3 +629,3 +630,3 +631,2 +632,0 +633,1 +634,0 +635,2 +636,0 +637,3 +638,3 +639,0 +640,0 +641,2 +642,0 +643,0 +644,3 +645,3 +646,0 +647,2 +648,1 +649,1 +650,0 +651,3 +652,1 +653,0 +654,2 +655,0 +656,0 +657,2 +658,0 +659,0 +660,0 +661,0 +662,3 +663,3 +664,1 +665,3 +666,3 +667,1 +668,2 +669,0 +670,2 +671,2 +672,3 +673,3 +674,0 +675,0 +676,3 +677,0 +678,3 +679,0 +680,2 +681,0 +682,1 +683,1 +684,3 +685,1 +686,3 +687,0 +688,1 +689,3 +690,1 +691,0 +692,0 +693,0 +694,2 +695,3 +696,0 +697,0 +698,1 +699,0 +700,0 +701,0 +702,2 +703,0 +704,0 +705,1 +706,0 +707,2 +708,2 +709,0 +710,3 +711,1 +712,0 +713,0 +714,3 +715,1 +716,0 +717,0 +718,0 +719,2 +720,3 +721,1 +722,2 +723,2 +724,2 +725,0 +726,0 +727,0 +728,3 +729,0 +730,0 +731,3 +732,2 +733,2 +734,1 +735,2 +736,0 +737,0 +738,0 +739,3 +740,0 +741,0 +742,0 +743,1 +744,0 +745,0 +746,1 +747,3 +748,3 +749,2 +750,3 +751,3 +752,0 +753,0 +754,3 +755,3 +756,3 +757,2 +758,2 +759,1 +760,3 +761,3 +762,3 +763,1 +764,3 +765,3 +766,3 +767,0 +768,0 +769,3 +770,2 +771,0 +772,0 +773,1 +774,0 +775,0 +776,0 +777,0 +778,0 +779,0 +780,3 +781,0 +782,0 +783,1 +784,3 +785,0 +786,0 +787,2 +788,0 +789,0 +790,1 +791,3 +792,2 +793,1 +794,3 +795,2 +796,2 +797,1 +798,3 +799,0 +800,0 +801,2 +802,2 +803,2 +804,3 +805,3 +806,0 +807,2 +808,2 +809,2 +810,2 +811,1 +812,1 +813,0 +814,2 +815,2 +816,3 +817,3 +818,3 +819,1 +820,1 +821,2 +822,1 +823,0 +824,0 +825,0 +826,0 +827,3 +828,1 +829,1 +830,3 +831,2 +832,1 +833,0 +834,0 +835,0 +836,3 +837,0 +838,3 +839,3 +840,1 +841,0 +842,2 +843,0 +844,0 +845,0 +846,1 +847,0 +848,3 +849,0 +850,0 +851,0 +852,0 +853,2 +854,0 +855,2 +856,0 +857,3 +858,1 +859,0 +860,0 +861,1 +862,3 +863,1 +864,1 +865,0 +866,2 +867,1 +868,3 +869,1 +870,2 +871,3 +872,3 +873,0 +874,0 +875,1 +876,3 +877,3 +878,3 +879,1 +880,3 +881,0 +882,3 +883,1 +884,1 +885,2 +886,0 +887,0 +888,0 +889,0 +890,0 +891,2 +892,0 +893,0 +894,0 +895,0 +896,2 +897,2 +898,2 +899,0 +900,0 +901,1 +902,0 +903,3 +904,2 +905,2 +906,2 +907,2 +908,0 +909,3 +910,0 +911,2 +912,1 +913,1 +914,2 +915,0 +916,2 +917,0 +918,3 +919,0 +920,0 +921,2 +922,0 +923,3 +924,0 +925,1 +926,0 +927,0 +928,3 +929,0 +930,2 +931,3 +932,1 +933,0 +934,3 +935,0 +936,3 +937,2 +938,3 +939,2 +940,0 +941,3 +942,0 +943,0 +944,0 +945,3 +946,3 +947,0 +948,2 +949,0 +950,0 +951,0 +952,1 +953,1 +954,0 +955,0 +956,3 +957,3 +958,2 +959,0 +960,1 +961,3 +962,0 +963,3 +964,0 +965,0 +966,0 +967,0 +968,2 +969,2 +970,2 +971,0 +972,0 +973,2 +974,1 +975,0 +976,1 +977,2 +978,2 +979,2 +980,3 +981,3 +982,3 +983,0 +984,3 +985,1 +986,0 +987,0 +988,0 +989,2 +990,1 +991,0 +992,0 +993,0 +994,3 +995,2 +996,3 +997,2 +998,0 +999,1 +1000,0 +1001,0 +1002,0 +1003,2 +1004,0 +1005,2 +1006,2 +1007,0 +1008,2 +1009,2 +1010,3 +1011,3 +1012,3 +1013,0 +1014,1 +1015,0 +1016,2 +1017,1 +1018,3 +1019,2 +1020,3 +1021,3 +1022,0 +1023,0 +1024,0 +1025,1 +1026,3 +1027,3 +1028,0 +1029,0 +1030,0 +1031,0 +1032,1 +1033,2 +1034,0 +1035,0 +1036,3 +1037,0 +1038,1 +1039,1 +1040,0 +1041,2 +1042,3 +1043,1 +1044,3 +1045,3 +1046,3 +1047,1 +1048,1 +1049,1 +1050,1 +1051,2 +1052,2 +1053,0 +1054,1 +1055,0 +1056,3 +1057,2 +1058,0 +1059,0 +1060,0 +1061,0 +1062,0 +1063,1 +1064,0 +1065,3 +1066,1 +1067,2 +1068,3 +1069,0 +1070,0 +1071,0 +1072,1 +1073,3 +1074,2 +1075,2 +1076,0 +1077,2 +1078,3 +1079,0 +1080,0 +1081,0 +1082,0 +1083,2 +1084,1 +1085,2 +1086,2 +1087,3 +1088,0 +1089,2 +1090,1 +1091,0 +1092,3 +1093,0 +1094,2 +1095,0 +1096,0 +1097,0 +1098,1 +1099,0 +1100,3 +1101,3 +1102,3 +1103,1 +1104,1 +1105,0 +1106,2 +1107,3 +1108,2 +1109,3 +1110,1 +1111,3 +1112,0 +1113,0 +1114,0 +1115,0 +1116,0 +1117,3 +1118,0 +1119,3 +1120,1 +1121,0 +1122,0 +1123,2 +1124,2 +1125,0 +1126,0 +1127,1 +1128,1 +1129,0 +1130,0 +1131,1 +1132,0 +1133,2 +1134,3 +1135,0 +1136,0 +1137,3 +1138,3 +1139,1 +1140,3 +1141,0 +1142,0 +1143,0 +1144,3 +1145,0 +1146,3 +1147,0 +1148,0 +1149,1 +1150,0 +1151,2 +1152,0 +1153,3 +1154,0 +1155,3 +1156,0 +1157,3 +1158,0 +1159,0 +1160,1 +1161,3 +1162,1 +1163,0 +1164,2 +1165,2 +1166,1 +1167,3 +1168,2 +1169,3 +1170,3 +1171,0 +1172,0 +1173,1 +1174,2 +1175,2 +1176,0 +1177,0 +1178,0 +1179,1 +1180,1 +1181,2 +1182,1 +1183,0 +1184,0 +1185,3 +1186,2 +1187,3 +1188,0 +1189,1 +1190,2 +1191,0 +1192,0 +1193,0 +1194,0 +1195,0 +1196,2 +1197,0 +1198,0 +1199,3 +1200,3 +1201,0 +1202,0 +1203,3 +1204,0 +1205,2 +1206,0 +1207,2 +1208,2 +1209,1 +1210,2 +1211,2 +1212,0 +1213,3 +1214,1 +1215,0 +1216,0 +1217,0 +1218,0 +1219,0 +1220,3 +1221,3 +1222,0 +1223,3 +1224,2 +1225,3 +1226,2 +1227,0 +1228,3 +1229,1 +1230,1 +1231,1 +1232,1 +1233,2 +1234,1 +1235,2 +1236,3 +1237,0 +1238,2 +1239,0 +1240,0 +1241,3 +1242,1 +1243,1 +1244,3 +1245,3 +1246,0 +1247,3 +1248,0 +1249,0 +1250,3 +1251,0 +1252,0 +1253,1 +1254,0 +1255,3 +1256,0 +1257,1 +1258,0 +1259,0 +1260,1 +1261,0 +1262,1 +1263,2 +1264,2 +1265,0 +1266,0 +1267,3 +1268,2 +1269,2 +1270,0 +1271,3 +1272,0 +1273,2 +1274,0 +1275,1 +1276,0 +1277,0 +1278,0 +1279,3 +1280,3 +1281,0 +1282,3 +1283,0 +1284,2 +1285,3 +1286,3 +1287,0 +1288,0 +1289,3 +1290,0 +1291,0 +1292,0 +1293,3 +1294,2 +1295,0 +1296,0 +1297,0 +1298,2 +1299,0 +1300,2 +1301,2 +1302,2 +1303,0 +1304,3 +1305,3 +1306,2 +1307,0 +1308,0 +1309,3 +1310,1 +1311,3 +1312,3 +1313,1 +1314,2 +1315,2 +1316,0 +1317,0 +1318,1 +1319,3 +1320,1 +1321,3 +1322,0 +1323,0 +1324,2 +1325,0 +1326,0 +1327,2 +1328,3 +1329,0 +1330,0 +1331,0 +1332,0 +1333,3 +1334,3 +1335,2 +1336,0 +1337,3 +1338,0 +1339,3 +1340,0 +1341,0 +1342,0 +1343,0 +1344,0 +1345,0 +1346,1 +1347,0 +1348,0 +1349,2 +1350,1 +1351,0 +1352,0 +1353,1 +1354,2 +1355,0 +1356,2 +1357,3 +1358,2 +1359,0 +1360,3 +1361,3 +1362,0 +1363,0 +1364,0 +1365,0 +1366,3 +1367,2 +1368,0 +1369,0 +1370,2 +1371,0 +1372,0 +1373,2 +1374,2 +1375,2 +1376,0 +1377,1 +1378,0 +1379,0 +1380,2 +1381,0 +1382,0 +1383,0 +1384,3 +1385,2 +1386,3 +1387,1 +1388,1 +1389,0 +1390,0 +1391,3 +1392,1 +1393,2 +1394,3 +1395,0 +1396,3 +1397,0 +1398,0 +1399,0 +1400,0 +1401,0 +1402,0 +1403,2 +1404,0 +1405,3 +1406,3 +1407,2 +1408,0 +1409,0 +1410,0 +1411,1 +1412,0 +1413,0 +1414,0 +1415,0 +1416,1 +1417,0 +1418,0 +1419,0 +1420,0 +1421,0 +1422,2 +1423,0 +1424,1 +1425,3 +1426,3 +1427,0 +1428,0 +1429,0 +1430,0 +1431,0 +1432,3 +1433,1 +1434,3 +1435,0 +1436,0 +1437,3 +1438,1 +1439,2 +1440,1 +1441,2 +1442,0 +1443,0 +1444,0 +1445,3 +1446,0 +1447,0 +1448,2 +1449,2 +1450,3 +1451,0 +1452,3 +1453,3 +1454,3 +1455,1 +1456,0 +1457,0 +1458,0 +1459,3 +1460,0 +1461,0 +1462,2 +1463,3 +1464,3 +1465,1 +1466,0 +1467,3 +1468,0 +1469,0 +1470,0 +1471,1 +1472,0 +1473,0 +1474,3 +1475,0 +1476,1 +1477,0 +1478,3 +1479,3 +1480,0 +1481,1 +1482,3 +1483,1 +1484,3 +1485,0 +1486,1 +1487,0 +1488,3 +1489,0 +1490,0 +1491,0 +1492,1 +1493,3 +1494,0 +1495,1 +1496,2 +1497,3 +1498,0 +1499,0 +1500,0 +1501,0 +1502,3 +1503,0 +1504,0 +1505,1 +1506,2 +1507,0 +1508,2 +1509,3 +1510,2 +1511,3 +1512,0 +1513,3 +1514,3 +1515,2 +1516,3 +1517,3 +1518,2 +1519,3 +1520,2 +1521,3 +1522,0 +1523,0 +1524,1 +1525,0 +1526,1 +1527,1 +1528,1 +1529,0 +1530,3 +1531,1 +1532,3 +1533,3 +1534,3 +1535,2 +1536,0 +1537,1 +1538,2 +1539,0 +1540,0 +1541,0 +1542,1 +1543,0 +1544,0 +1545,1 +1546,1 +1547,1 +1548,2 +1549,2 +1550,2 +1551,0 +1552,1 +1553,0 +1554,0 +1555,3 +1556,0 +1557,2 +1558,1 +1559,0 +1560,3 +1561,2 +1562,3 +1563,0 +1564,2 +1565,3 +1566,0 +1567,3 +1568,0 +1569,0 +1570,3 +1571,3 +1572,3 +1573,0 +1574,2 +1575,1 +1576,3 +1577,1 +1578,1 +1579,0 +1580,1 +1581,2 +1582,0 +1583,1 +1584,0 +1585,0 +1586,0 +1587,0 +1588,0 +1589,0 +1590,0 +1591,3 +1592,2 +1593,2 +1594,0 +1595,0 +1596,0 +1597,3 +1598,3 +1599,0 +1600,1 +1601,1 +1602,0 +1603,0 +1604,3 +1605,0 +1606,2 +1607,0 +1608,1 +1609,1 +1610,2 +1611,3 +1612,0 +1613,0 +1614,0 +1615,0 +1616,2 +1617,3 +1618,3 +1619,0 +1620,2 +1621,3 +1622,3 +1623,1 +1624,0 +1625,3 +1626,2 +1627,0 +1628,0 +1629,3 +1630,3 +1631,0 +1632,2 +1633,0 +1634,0 +1635,1 +1636,2 +1637,3 +1638,1 +1639,0 +1640,0 +1641,0 +1642,3 +1643,1 +1644,0 +1645,0 +1646,2 +1647,3 +1648,0 +1649,2 +1650,3 +1651,1 +1652,1 +1653,3 +1654,1 +1655,2 +1656,0 +1657,1 +1658,0 +1659,1 +1660,0 +1661,3 +1662,2 +1663,0 +1664,1 +1665,2 +1666,0 +1667,3 +1668,2 +1669,0 +1670,3 +1671,2 +1672,0 +1673,0 +1674,2 +1675,0 +1676,0 +1677,3 +1678,1 +1679,1 +1680,3 +1681,2 +1682,3 +1683,0 +1684,2 +1685,2 +1686,0 +1687,2 +1688,0 +1689,0 +1690,1 +1691,3 +1692,0 +1693,3 +1694,1 +1695,3 +1696,3 +1697,0 +1698,0 +1699,0 +1700,0 +1701,0 +1702,1 +1703,0 +1704,1 +1705,0 +1706,1 +1707,0 +1708,2 +1709,0 +1710,3 +1711,2 +1712,0 +1713,2 +1714,0 +1715,1 +1716,0 +1717,0 +1718,3 +1719,0 +1720,1 +1721,3 +1722,2 +1723,3 +1724,0 +1725,1 +1726,1 +1727,1 +1728,0 +1729,0 +1730,2 +1731,0 +1732,1 +1733,1 +1734,3 +1735,0 +1736,3 +1737,0 +1738,2 +1739,0 +1740,0 +1741,2 +1742,3 +1743,1 +1744,2 +1745,1 +1746,3 +1747,1 +1748,0 +1749,0 +1750,0 +1751,0 +1752,2 +1753,0 +1754,0 +1755,2 +1756,0 +1757,3 +1758,3 +1759,1 +1760,0 +1761,0 +1762,0 +1763,3 +1764,0 +1765,0 +1766,3 +1767,1 +1768,0 +1769,1 +1770,3 +1771,3 +1772,2 +1773,3 +1774,1 +1775,2 +1776,1 +1777,3 +1778,0 +1779,0 +1780,1 +1781,3 +1782,0 +1783,0 +1784,0 +1785,0 +1786,0 +1787,2 +1788,0 +1789,1 +1790,2 +1791,0 +1792,2 +1793,0 +1794,2 +1795,0 +1796,3 +1797,0 +1798,1 +1799,0 +1800,2 +1801,2 +1802,0 +1803,0 +1804,3 +1805,0 +1806,3 +1807,1 +1808,3 +1809,0 +1810,1 +1811,0 +1812,3 +1813,2 +1814,0 +1815,1 +1816,3 +1817,0 +1818,0 +1819,2 +1820,3 +1821,0 +1822,0 +1823,0 +1824,0 +1825,0 +1826,2 +1827,1 +1828,0 +1829,2 +1830,0 +1831,3 +1832,2 +1833,2 +1834,2 +1835,0 +1836,2 +1837,0 +1838,0 +1839,0 +1840,0 +1841,0 +1842,0 +1843,0 +1844,3 +1845,2 +1846,1 +1847,1 +1848,0 +1849,1 +1850,2 +1851,3 +1852,3 +1853,0 +1854,0 +1855,2 +1856,0 +1857,0 +1858,3 +1859,1 +1860,0 +1861,1 +1862,2 +1863,0 +1864,1 +1865,0 +1866,0 +1867,1 +1868,0 +1869,0 +1870,0 +1871,2 +1872,2 +1873,1 +1874,0 +1875,3 +1876,0 +1877,3 +1878,0 +1879,2 +1880,0 +1881,1 +1882,2 +1883,0 +1884,3 +1885,0 +1886,2 +1887,0 +1888,0 +1889,1 +1890,0 +1891,2 +1892,2 +1893,0 +1894,0 +1895,1 +1896,0 +1897,0 +1898,2 +1899,0 +1900,0 +1901,3 +1902,2 +1903,2 +1904,2 +1905,0 +1906,0 +1907,1 +1908,0 +1909,3 +1910,0 +1911,2 +1912,0 +1913,1 +1914,0 +1915,2 +1916,0 +1917,0 +1918,0 +1919,0 +1920,1 +1921,3 +1922,0 +1923,3 +1924,3 +1925,1 +1926,0 +1927,0 +1928,0 +1929,2 +1930,2 +1931,0 +1932,0 +1933,0 +1934,1 +1935,1 +1936,0 +1937,3 +1938,1 +1939,3 +1940,1 +1941,0 +1942,2 +1943,2 +1944,0 +1945,0 +1946,1 +1947,1 +1948,1 +1949,0 +1950,0 +1951,0 +1952,0 +1953,0 +1954,0 +1955,0 +1956,1 +1957,2 +1958,3 +1959,3 +1960,0 +1961,1 +1962,2 +1963,2 +1964,3 +1965,0 +1966,2 +1967,2 +1968,3 +1969,0 +1970,1 +1971,0 +1972,0 +1973,0 +1974,3 +1975,2 +1976,0 +1977,3 +1978,1 +1979,0 +1980,2 +1981,1 +1982,0 +1983,0 +1984,1 +1985,0 +1986,2 +1987,0 +1988,3 +1989,0 +1990,0 +1991,3 +1992,0 +1993,2 +1994,0 +1995,0 +1996,3 +1997,2 +1998,2 +1999,0 +2000,0 +2001,0 +2002,0 +2003,0 +2004,1 +2005,0 +2006,0 +2007,2 +2008,0 +2009,3 +2010,0 +2011,1 +2012,1 +2013,0 +2014,1 +2015,0 +2016,3 +2017,0 +2018,0 +2019,2 +2020,2 +2021,2 +2022,0 +2023,3 +2024,1 +2025,0 +2026,0 +2027,0 +2028,0 +2029,3 +2030,2 +2031,0 +2032,2 +2033,0 +2034,3 +2035,0 +2036,0 +2037,2 +2038,0 +2039,0 +2040,2 +2041,0 +2042,0 +2043,0 +2044,2 +2045,0 +2046,3 +2047,0 +2048,0 +2049,2 +2050,0 +2051,0 +2052,3 +2053,0 +2054,0 +2055,2 +2056,0 +2057,1 +2058,1 +2059,0 +2060,0 +2061,3 +2062,2 +2063,0 +2064,0 +2065,0 +2066,2 +2067,0 +2068,0 +2069,0 +2070,2 +2071,3 +2072,0 +2073,1 +2074,0 +2075,0 +2076,0 +2077,3 +2078,0 +2079,1 +2080,0 +2081,1 +2082,3 +2083,0 +2084,0 +2085,3 +2086,3 +2087,0 +2088,0 +2089,3 +2090,1 +2091,3 +2092,2 +2093,2 +2094,2 +2095,1 +2096,0 +2097,3 +2098,0 +2099,1 +2100,0 +2101,0 +2102,1 +2103,1 +2104,2 +2105,0 +2106,0 +2107,2 +2108,0 +2109,2 +2110,2 +2111,0 +2112,1 +2113,0 +2114,0 +2115,1 +2116,2 +2117,1 +2118,0 +2119,2 +2120,2 +2121,0 +2122,0 +2123,0 +2124,0 +2125,0 +2126,0 +2127,0 +2128,0 +2129,3 +2130,3 +2131,1 +2132,0 +2133,1 +2134,2 +2135,0 +2136,1 +2137,0 +2138,0 +2139,2 +2140,3 +2141,3 +2142,0 +2143,0 +2144,0 +2145,3 +2146,0 +2147,0 +2148,3 +2149,3 +2150,2 +2151,1 +2152,1 +2153,0 +2154,2 +2155,0 +2156,2 +2157,2 +2158,2 +2159,1 +2160,0 +2161,2 +2162,0 +2163,3 +2164,3 +2165,3 +2166,1 +2167,3 +2168,0 +2169,1 +2170,3 +2171,3 +2172,2 +2173,0 +2174,0 +2175,0 +2176,3 +2177,2 +2178,0 +2179,0 +2180,0 +2181,3 +2182,3 +2183,1 +2184,0 +2185,1 +2186,0 +2187,1 +2188,3 +2189,1 +2190,0 +2191,1 +2192,3 +2193,3 +2194,1 +2195,1 +2196,3 +2197,3 +2198,2 +2199,2 +2200,1 +2201,0 +2202,1 +2203,3 +2204,0 +2205,3 +2206,1 +2207,0 +2208,0 +2209,3 +2210,0 +2211,3 +2212,0 +2213,3 +2214,1 +2215,0 +2216,2 +2217,0 +2218,0 +2219,2 +2220,0 +2221,3 +2222,2 +2223,0 +2224,0 +2225,2 +2226,0 +2227,0 +2228,1 +2229,0 +2230,2 +2231,0 +2232,3 +2233,3 +2234,0 +2235,0 +2236,0 +2237,0 +2238,2 +2239,0 +2240,1 +2241,1 +2242,0 +2243,0 +2244,0 +2245,2 +2246,0 +2247,2 +2248,3 +2249,0 +2250,2 +2251,0 +2252,0 +2253,0 +2254,0 +2255,3 +2256,1 +2257,0 +2258,3 +2259,0 +2260,1 +2261,3 +2262,3 +2263,2 +2264,1 +2265,2 +2266,2 +2267,2 +2268,0 +2269,2 +2270,3 +2271,0 +2272,3 +2273,3 +2274,1 +2275,0 +2276,3 +2277,0 +2278,0 +2279,2 +2280,0 +2281,0 +2282,0 +2283,1 +2284,0 +2285,2 +2286,3 +2287,2 +2288,3 +2289,2 +2290,0 +2291,0 +2292,0 +2293,3 +2294,3 +2295,0 +2296,2 +2297,0 +2298,3 +2299,0 +2300,0 +2301,3 +2302,0 +2303,0 +2304,0 +2305,3 +2306,1 +2307,0 +2308,0 +2309,1 +2310,2 +2311,0 +2312,0 +2313,1 +2314,2 +2315,2 +2316,0 +2317,2 +2318,3 +2319,0 +2320,2 +2321,0 +2322,0 +2323,0 +2324,3 +2325,1 +2326,3 +2327,0 +2328,0 +2329,3 +2330,2 +2331,0 +2332,1 +2333,0 +2334,3 +2335,1 +2336,0 +2337,3 +2338,0 +2339,1 +2340,0 +2341,0 +2342,0 +2343,0 +2344,0 +2345,1 +2346,1 +2347,3 +2348,0 +2349,0 +2350,0 +2351,0 +2352,0 +2353,2 +2354,3 +2355,3 +2356,3 +2357,1 +2358,0 +2359,0 +2360,2 +2361,0 +2362,3 +2363,2 +2364,2 +2365,3 +2366,3 +2367,3 +2368,0 +2369,0 +2370,3 +2371,1 +2372,0 +2373,0 +2374,1 +2375,1 +2376,3 +2377,0 +2378,1 +2379,1 +2380,2 +2381,2 +2382,2 +2383,0 +2384,1 +2385,3 +2386,0 +2387,1 +2388,3 +2389,3 +2390,0 +2391,3 +2392,0 +2393,2 +2394,3 +2395,3 +2396,3 +2397,0 +2398,3 +2399,3 +2400,0 +2401,3 +2402,0 +2403,0 +2404,1 +2405,3 +2406,3 +2407,0 +2408,1 +2409,0 +2410,3 +2411,2 +2412,1 +2413,3 +2414,0 +2415,0 +2416,3 +2417,0 +2418,2 +2419,0 +2420,0 +2421,1 +2422,3 +2423,2 +2424,1 +2425,3 +2426,3 +2427,2 +2428,0 +2429,2 +2430,3 +2431,1 +2432,2 +2433,0 +2434,2 +2435,1 +2436,3 +2437,3 +2438,2 +2439,0 +2440,3 +2441,0 +2442,2 +2443,3 +2444,1 +2445,0 +2446,3 +2447,2 +2448,0 +2449,0 +2450,3 +2451,3 +2452,0 +2453,2 +2454,3 +2455,0 +2456,0 +2457,0 +2458,1 +2459,2 +2460,0 +2461,0 +2462,2 +2463,3 +2464,0 +2465,0 +2466,2 +2467,3 +2468,3 +2469,3 +2470,0 +2471,0 +2472,0 +2473,2 +2474,3 +2475,0 +2476,0 +2477,0 +2478,3 +2479,3 +2480,1 +2481,0 +2482,3 +2483,0 +2484,3 +2485,1 +2486,3 +2487,0 +2488,3 +2489,1 +2490,2 +2491,0 +2492,2 +2493,1 +2494,0 +2495,0 +2496,1 +2497,2 +2498,1 +2499,0 +2500,2 +2501,3 +2502,3 +2503,3 +2504,2 +2505,0 +2506,0 +2507,0 +2508,2 +2509,3 +2510,0 +2511,0 +2512,3 +2513,3 +2514,1 +2515,0 +2516,3 +2517,3 +2518,1 +2519,1 +2520,0 +2521,0 +2522,2 +2523,1 +2524,0 +2525,0 +2526,3 +2527,0 +2528,0 +2529,0 +2530,0 +2531,0 +2532,2 +2533,0 +2534,0 +2535,1 +2536,2 +2537,0 +2538,0 +2539,3 +2540,0 +2541,2 +2542,3 +2543,2 +2544,2 +2545,1 +2546,3 +2547,0 +2548,1 +2549,0 +2550,3 +2551,3 +2552,2 +2553,0 +2554,2 +2555,0 +2556,0 +2557,2 +2558,0 +2559,0 +2560,0 +2561,3 +2562,2 +2563,2 +2564,0 +2565,3 +2566,2 +2567,3 +2568,0 +2569,2 +2570,2 +2571,1 +2572,0 +2573,2 +2574,0 +2575,1 +2576,1 +2577,0 +2578,2 +2579,0 +2580,1 +2581,1 +2582,3 +2583,3 +2584,3 +2585,1 +2586,0 +2587,2 +2588,3 +2589,3 +2590,2 +2591,2 +2592,2 +2593,0 +2594,1 +2595,0 +2596,1 +2597,0 +2598,1 +2599,0 +2600,3 +2601,3 +2602,3 +2603,0 +2604,2 +2605,1 +2606,2 +2607,0 +2608,2 +2609,3 +2610,0 +2611,3 +2612,0 +2613,0 +2614,0 +2615,0 +2616,0 +2617,3 +2618,0 +2619,2 +2620,1 +2621,3 +2622,2 +2623,1 +2624,1 +2625,2 +2626,0 +2627,0 +2628,1 +2629,3 +2630,0 +2631,0 +2632,0 +2633,1 +2634,2 +2635,1 +2636,0 +2637,3 +2638,3 +2639,1 +2640,3 +2641,0 +2642,2 +2643,2 +2644,3 +2645,0 +2646,0 +2647,0 +2648,1 +2649,0 +2650,2 +2651,0 +2652,0 +2653,1 +2654,0 +2655,0 +2656,2 +2657,2 +2658,3 +2659,0 +2660,2 +2661,1 +2662,0 +2663,3 +2664,2 +2665,3 +2666,0 +2667,2 +2668,2 +2669,0 +2670,3 +2671,1 +2672,3 +2673,0 +2674,0 +2675,0 +2676,0 +2677,1 +2678,3 +2679,0 +2680,3 +2681,2 +2682,2 +2683,0 +2684,3 +2685,1 +2686,0 +2687,2 +2688,0 +2689,0 +2690,1 +2691,0 +2692,2 +2693,2 +2694,2 +2695,1 +2696,3 +2697,2 +2698,0 +2699,3 +2700,2 +2701,3 +2702,0 +2703,1 +2704,0 +2705,2 +2706,3 +2707,1 +2708,0 +2709,2 +2710,1 +2711,0 +2712,0 +2713,2 +2714,0 +2715,3 +2716,1 +2717,0 +2718,1 +2719,3 +2720,3 +2721,0 +2722,0 +2723,0 +2724,3 +2725,1 +2726,0 +2727,0 +2728,3 +2729,2 +2730,0 +2731,0 +2732,3 +2733,2 +2734,0 +2735,0 +2736,2 +2737,1 +2738,0 +2739,0 +2740,0 +2741,0 +2742,0 +2743,1 +2744,1 +2745,0 +2746,3 +2747,1 +2748,3 +2749,0 +2750,3 +2751,3 +2752,0 +2753,0 +2754,2 +2755,2 +2756,0 +2757,2 +2758,2 +2759,1 +2760,3 +2761,0 +2762,0 +2763,0 +2764,0 +2765,0 +2766,1 +2767,3 +2768,0 +2769,0 +2770,3 +2771,0 +2772,3 +2773,0 +2774,0 +2775,0 +2776,0 +2777,1 +2778,3 +2779,3 +2780,0 +2781,2 +2782,0 +2783,2 +2784,2 +2785,0 +2786,2 +2787,0 +2788,3 +2789,0 +2790,0 +2791,0 +2792,0 +2793,1 +2794,0 +2795,0 +2796,2 +2797,0 +2798,2 +2799,1 +2800,3 +2801,0 +2802,0 +2803,0 +2804,3 +2805,3 +2806,0 +2807,3 +2808,3 +2809,0 +2810,1 +2811,0 +2812,0 +2813,1 +2814,3 +2815,3 +2816,0 +2817,0 +2818,2 +2819,3 +2820,2 +2821,2 +2822,2 +2823,2 +2824,0 +2825,0 +2826,0 +2827,0 +2828,3 +2829,2 +2830,0 +2831,0 +2832,3 +2833,0 +2834,2 +2835,0 +2836,0 +2837,3 +2838,2 +2839,2 +2840,2 +2841,3 +2842,1 +2843,1 +2844,0 +2845,2 +2846,2 +2847,3 +2848,0 +2849,0 +2850,3 +2851,0 +2852,0 +2853,3 +2854,3 +2855,0 +2856,0 +2857,0 +2858,1 +2859,0 +2860,2 +2861,0 +2862,0 +2863,0 +2864,1 +2865,1 +2866,0 +2867,3 +2868,0 +2869,3 +2870,3 +2871,1 +2872,3 +2873,2 +2874,2 +2875,0 +2876,0 +2877,3 +2878,2 +2879,3 +2880,0 +2881,3 +2882,3 +2883,2 +2884,3 +2885,3 +2886,3 +2887,1 +2888,1 +2889,1 +2890,0 +2891,3 +2892,0 +2893,3 +2894,0 +2895,3 +2896,3 +2897,1 +2898,3 +2899,0 +2900,3 +2901,3 +2902,3 +2903,2 +2904,3 +2905,2 +2906,2 +2907,0 +2908,3 +2909,3 +2910,1 +2911,1 +2912,0 +2913,2 +2914,3 +2915,2 +2916,3 +2917,3 +2918,3 +2919,0 +2920,0 +2921,0 +2922,3 +2923,1 +2924,0 +2925,0 +2926,0 +2927,0 +2928,2 +2929,3 +2930,2 +2931,1 +2932,0 +2933,2 +2934,0 +2935,0 +2936,0 +2937,2 +2938,0 +2939,0 +2940,2 +2941,3 +2942,0 +2943,3 +2944,1 +2945,0 +2946,0 +2947,0 +2948,2 +2949,0 +2950,0 +2951,2 +2952,0 +2953,2 +2954,1 +2955,0 +2956,2 +2957,3 +2958,0 +2959,0 +2960,0 +2961,0 +2962,3 +2963,3 +2964,0 +2965,2 +2966,2 +2967,1 +2968,1 +2969,0 +2970,0 +2971,1 +2972,0 +2973,0 +2974,2 +2975,2 +2976,3 +2977,3 +2978,1 +2979,0 +2980,2 +2981,0 +2982,3 +2983,1 +2984,2 +2985,0 +2986,2 +2987,3 +2988,0 +2989,2 +2990,0 +2991,0 +2992,0 +2993,0 +2994,0 +2995,3 +2996,0 +2997,0 +2998,3 +2999,0 +3000,0 +3001,0 +3002,3 +3003,2 +3004,0 +3005,3 +3006,1 +3007,0 +3008,3 +3009,1 +3010,2 +3011,1 +3012,0 +3013,3 +3014,1 +3015,3 +3016,0 +3017,0 +3018,0 +3019,3 +3020,2 +3021,0 +3022,1 +3023,3 +3024,0 +3025,3 +3026,0 +3027,1 +3028,0 +3029,3 +3030,3 +3031,0 +3032,3 +3033,2 +3034,0 +3035,3 +3036,3 +3037,0 +3038,3 +3039,0 +3040,3 +3041,1 +3042,0 +3043,0 +3044,1 +3045,0 +3046,0 +3047,0 +3048,0 +3049,0 +3050,0 +3051,0 +3052,0 +3053,1 +3054,0 +3055,2 +3056,3 +3057,2 +3058,0 +3059,3 +3060,0 +3061,3 +3062,3 +3063,0 +3064,3 +3065,3 +3066,0 +3067,3 +3068,1 +3069,0 +3070,0 +3071,3 +3072,3 +3073,3 +3074,3 +3075,0 +3076,0 +3077,0 +3078,0 +3079,0 +3080,3 +3081,3 +3082,0 +3083,0 +3084,1 +3085,0 +3086,3 +3087,0 +3088,3 +3089,2 +3090,0 +3091,2 +3092,1 +3093,0 +3094,0 +3095,1 +3096,0 +3097,3 +3098,3 +3099,2 +3100,2 +3101,0 +3102,0 +3103,0 +3104,0 +3105,1 +3106,0 +3107,0 +3108,0 +3109,0 +3110,3 +3111,0 +3112,2 +3113,1 +3114,0 +3115,0 +3116,0 +3117,0 +3118,0 +3119,0 +3120,1 +3121,2 +3122,0 +3123,0 +3124,0 +3125,2 +3126,0 +3127,2 +3128,2 +3129,0 +3130,0 +3131,3 +3132,1 +3133,0 +3134,0 +3135,3 +3136,3 +3137,3 +3138,1 +3139,0 +3140,0 +3141,1 +3142,2 +3143,0 +3144,3 +3145,0 +3146,3 +3147,0 +3148,0 +3149,0 +3150,1 +3151,0 +3152,0 +3153,0 +3154,0 +3155,3 +3156,3 +3157,3 +3158,1 +3159,2 +3160,2 +3161,2 +3162,1 +3163,3 +3164,3 +3165,3 +3166,3 +3167,1 +3168,0 +3169,0 +3170,2 +3171,1 +3172,3 +3173,3 +3174,2 +3175,0 +3176,0 +3177,1 +3178,3 +3179,1 +3180,2 +3181,1 +3182,2 +3183,2 +3184,0 +3185,3 +3186,3 +3187,1 +3188,0 +3189,0 +3190,0 +3191,0 +3192,0 +3193,3 +3194,1 +3195,2 +3196,0 +3197,0 +3198,0 +3199,2 +3200,0 +3201,1 +3202,3 +3203,0 +3204,2 +3205,0 +3206,3 +3207,0 +3208,1 +3209,3 +3210,0 +3211,2 +3212,2 +3213,3 +3214,3 +3215,0 +3216,0 +3217,2 +3218,0 +3219,0 +3220,1 +3221,0 +3222,3 +3223,2 +3224,0 +3225,0 +3226,0 +3227,3 +3228,0 +3229,2 +3230,0 +3231,3 +3232,3 +3233,1 +3234,3 +3235,0 +3236,2 +3237,3 +3238,1 +3239,3 +3240,2 +3241,2 +3242,3 +3243,0 +3244,3 +3245,1 +3246,2 +3247,0 +3248,1 +3249,3 +3250,3 +3251,0 +3252,0 +3253,3 +3254,0 +3255,3 +3256,2 +3257,3 +3258,2 +3259,0 +3260,3 +3261,1 +3262,0 +3263,3 +3264,0 +3265,0 +3266,2 +3267,3 +3268,3 +3269,1 +3270,0 +3271,1 +3272,0 +3273,0 +3274,0 +3275,0 +3276,0 +3277,0 +3278,0 +3279,0 +3280,2 +3281,1 +3282,3 +3283,0 +3284,0 +3285,1 +3286,3 +3287,0 +3288,3 +3289,0 +3290,0 +3291,0 +3292,1 +3293,3 +3294,1 +3295,1 +3296,0 +3297,0 +3298,1 +3299,0 +3300,1 +3301,0 +3302,0 +3303,0 +3304,0 +3305,0 +3306,0 +3307,0 +3308,0 +3309,3 +3310,0 +3311,0 +3312,1 +3313,1 +3314,2 +3315,2 +3316,0 +3317,0 +3318,3 +3319,1 +3320,2 +3321,0 +3322,3 +3323,0 +3324,2 +3325,0 +3326,0 +3327,0 +3328,3 +3329,2 +3330,2 +3331,0 +3332,3 +3333,0 +3334,2 +3335,3 +3336,0 +3337,0 +3338,1 +3339,2 +3340,3 +3341,0 +3342,0 +3343,0 +3344,2 +3345,1 +3346,2 +3347,0 +3348,0 +3349,2 +3350,2 +3351,0 +3352,0 +3353,3 +3354,0 +3355,1 +3356,3 +3357,0 +3358,0 +3359,3 +3360,0 +3361,3 +3362,0 +3363,3 +3364,0 +3365,0 +3366,2 +3367,1 +3368,3 +3369,1 +3370,1 +3371,0 +3372,3 +3373,3 +3374,2 +3375,3 +3376,0 +3377,1 +3378,3 +3379,1 +3380,0 +3381,0 +3382,3 +3383,2 +3384,0 +3385,3 +3386,0 +3387,2 +3388,1 +3389,3 +3390,0 +3391,0 +3392,3 +3393,0 +3394,2 +3395,0 +3396,0 +3397,0 +3398,3 +3399,3 +3400,3 +3401,2 +3402,2 +3403,1 +3404,0 +3405,3 +3406,2 +3407,0 +3408,0 +3409,3 +3410,2 +3411,1 +3412,3 +3413,0 +3414,1 +3415,0 +3416,3 +3417,0 +3418,0 +3419,3 +3420,3 +3421,3 +3422,0 +3423,0 +3424,1 +3425,0 +3426,0 +3427,3 +3428,2 +3429,2 +3430,0 +3431,0 +3432,0 +3433,0 +3434,3 +3435,2 +3436,1 +3437,1 +3438,0 +3439,0 +3440,3 +3441,3 +3442,0 +3443,2 +3444,0 +3445,0 +3446,3 +3447,1 +3448,1 +3449,3 +3450,0 +3451,3 +3452,3 +3453,0 +3454,0 +3455,3 +3456,2 +3457,0 +3458,3 +3459,0 +3460,1 +3461,0 +3462,0 +3463,1 +3464,0 +3465,0 +3466,1 +3467,0 +3468,2 +3469,3 +3470,3 +3471,2 +3472,3 +3473,0 +3474,2 +3475,0 +3476,0 +3477,3 +3478,2 +3479,0 +3480,2 +3481,3 +3482,3 +3483,0 +3484,2 +3485,1 +3486,1 +3487,0 +3488,0 +3489,0 +3490,0 +3491,2 +3492,0 +3493,3 +3494,1 +3495,0 +3496,0 +3497,0 +3498,2 +3499,1 +3500,0 +3501,1 +3502,2 +3503,0 +3504,2 +3505,0 +3506,0 +3507,2 +3508,2 +3509,0 +3510,3 +3511,2 +3512,2 +3513,2 +3514,0 +3515,0 +3516,0 +3517,0 +3518,0 +3519,3 +3520,2 +3521,2 +3522,3 +3523,0 +3524,2 +3525,2 +3526,0 +3527,0 +3528,1 +3529,0 +3530,2 +3531,3 +3532,0 +3533,0 +3534,2 +3535,1 +3536,2 +3537,3 +3538,0 +3539,3 +3540,2 +3541,0 +3542,0 +3543,2 +3544,0 +3545,1 +3546,1 +3547,2 +3548,1 +3549,0 +3550,2 +3551,3 +3552,2 +3553,3 +3554,0 +3555,2 +3556,3 +3557,2 +3558,3 +3559,0 +3560,2 +3561,0 +3562,2 +3563,2 +3564,3 +3565,0 +3566,0 +3567,1 +3568,1 +3569,3 +3570,2 +3571,2 +3572,0 +3573,3 +3574,0 +3575,0 +3576,0 +3577,0 +3578,3 +3579,0 +3580,1 +3581,2 +3582,0 +3583,0 +3584,2 +3585,3 +3586,1 +3587,3 +3588,0 +3589,3 +3590,3 +3591,0 +3592,2 +3593,3 +3594,2 +3595,0 +3596,0 +3597,0 +3598,0 +3599,0 +3600,3 +3601,3 +3602,3 +3603,2 +3604,0 +3605,0 +3606,2 +3607,0 +3608,0 +3609,0 +3610,3 +3611,0 +3612,3 +3613,2 +3614,1 +3615,1 +3616,0 +3617,0 +3618,0 +3619,2 +3620,2 +3621,1 +3622,3 +3623,1 +3624,3 +3625,3 +3626,3 +3627,3 +3628,3 +3629,0 +3630,0 +3631,3 +3632,0 +3633,1 +3634,0 +3635,0 +3636,3 +3637,2 +3638,3 +3639,0 +3640,2 +3641,3 +3642,0 +3643,0 +3644,2 +3645,2 +3646,0 +3647,0 +3648,3 +3649,3 +3650,2 +3651,0 +3652,2 +3653,0 +3654,0 +3655,0 +3656,0 +3657,3 +3658,0 +3659,2 +3660,1 +3661,0 +3662,1 +3663,0 +3664,0 +3665,3 +3666,1 +3667,0 +3668,2 +3669,2 +3670,2 +3671,1 +3672,0 +3673,0 +3674,1 +3675,2 +3676,0 +3677,1 +3678,2 +3679,0 +3680,0 +3681,1 +3682,0 +3683,3 +3684,0 +3685,3 +3686,0 +3687,0 +3688,2 +3689,2 +3690,1 +3691,0 +3692,1 +3693,0 +3694,2 +3695,1 +3696,0 +3697,0 +3698,3 +3699,2 +3700,3 +3701,1 +3702,1 +3703,1 +3704,3 +3705,0 +3706,0 +3707,0 +3708,2 +3709,0 +3710,0 +3711,3 +3712,0 +3713,0 +3714,2 +3715,1 +3716,3 +3717,1 +3718,3 +3719,1 +3720,0 +3721,1 +3722,1 +3723,1 +3724,0 +3725,0 +3726,0 +3727,0 +3728,0 +3729,0 +3730,3 +3731,0 +3732,3 +3733,0 +3734,0 +3735,3 +3736,0 +3737,3 +3738,0 +3739,3 +3740,2 +3741,0 +3742,3 +3743,0 +3744,3 +3745,0 +3746,0 +3747,2 +3748,2 +3749,1 +3750,0 +3751,0 +3752,3 +3753,2 +3754,3 +3755,2 +3756,3 +3757,0 +3758,3 +3759,2 +3760,0 +3761,0 +3762,0 +3763,0 +3764,0 +3765,3 +3766,3 +3767,0 +3768,2 +3769,0 +3770,3 +3771,0 +3772,0 +3773,0 +3774,1 +3775,2 +3776,0 +3777,0 +3778,0 +3779,1 +3780,1 +3781,0 +3782,3 +3783,1 +3784,2 +3785,3 +3786,0 +3787,0 +3788,2 +3789,0 +3790,2 +3791,0 +3792,2 +3793,2 +3794,0 +3795,0 +3796,0 +3797,0 +3798,1 +3799,0 +3800,0 +3801,1 +3802,3 +3803,2 +3804,0 +3805,1 +3806,2 +3807,0 +3808,3 +3809,0 +3810,2 +3811,3 +3812,2 +3813,1 +3814,2 +3815,0 +3816,0 +3817,0 +3818,3 +3819,0 +3820,2 +3821,2 +3822,0 +3823,3 +3824,0 +3825,0 +3826,2 +3827,0 +3828,0 +3829,1 +3830,0 +3831,2 +3832,3 +3833,3 +3834,0 +3835,0 +3836,0 +3837,0 +3838,0 +3839,3 +3840,2 +3841,3 +3842,3 +3843,3 +3844,0 +3845,2 +3846,0 +3847,0 +3848,0 +3849,1 +3850,0 +3851,3 +3852,0 +3853,0 +3854,3 +3855,0 +3856,2 +3857,0 +3858,0 +3859,3 +3860,2 +3861,0 +3862,0 +3863,0 +3864,1 +3865,0 +3866,3 +3867,0 +3868,0 +3869,3 +3870,0 +3871,2 +3872,1 +3873,2 +3874,3 +3875,0 +3876,0 +3877,0 +3878,0 +3879,0 +3880,0 +3881,3 +3882,1 +3883,1 +3884,3 +3885,0 +3886,2 +3887,1 +3888,0 +3889,0 +3890,2 +3891,2 +3892,0 +3893,0 +3894,0 +3895,0 +3896,0 +3897,3 +3898,2 +3899,0 +3900,2 +3901,1 +3902,0 +3903,3 +3904,0 +3905,3 +3906,0 +3907,0 +3908,0 +3909,3 +3910,2 +3911,1 +3912,3 +3913,0 +3914,0 +3915,2 +3916,0 +3917,0 +3918,3 +3919,3 +3920,0 +3921,2 +3922,1 +3923,1 +3924,3 +3925,0 +3926,1 +3927,2 +3928,2 +3929,0 +3930,0 +3931,3 +3932,2 +3933,0 +3934,0 +3935,2 +3936,0 +3937,1 +3938,0 +3939,1 +3940,0 +3941,1 +3942,1 +3943,0 +3944,1 +3945,2 +3946,2 +3947,0 +3948,1 +3949,3 +3950,2 +3951,2 +3952,0 +3953,0 +3954,0 +3955,1 +3956,0 +3957,0 +3958,1 +3959,0 +3960,0 +3961,0 +3962,3 +3963,2 +3964,2 +3965,3 +3966,0 +3967,2 +3968,0 +3969,1 +3970,3 +3971,0 +3972,0 +3973,0 +3974,3 +3975,0 +3976,0 +3977,3 +3978,3 +3979,1 +3980,3 +3981,0 +3982,2 +3983,2 +3984,1 +3985,1 +3986,3 +3987,1 +3988,2 +3989,3 +3990,0 +3991,2 +3992,2 +3993,0 +3994,0 +3995,0 +3996,0 +3997,0 +3998,2 +3999,0 +4000,0 +4001,3 +4002,1 +4003,2 +4004,0 +4005,1 +4006,3 +4007,1 +4008,1 +4009,1 +4010,2 +4011,2 +4012,3 +4013,3 +4014,3 +4015,3 +4016,2 +4017,2 +4018,3 +4019,0 +4020,0 +4021,0 +4022,0 +4023,0 +4024,3 +4025,2 +4026,2 +4027,0 +4028,0 +4029,0 +4030,0 +4031,1 +4032,0 +4033,2 +4034,0 +4035,3 +4036,1 +4037,2 +4038,2 +4039,3 +4040,0 +4041,0 +4042,0 +4043,0 +4044,2 +4045,2 +4046,2 +4047,3 +4048,0 +4049,3 +4050,2 +4051,3 +4052,2 +4053,2 +4054,3 +4055,0 +4056,0 +4057,0 +4058,0 +4059,0 +4060,3 +4061,0 +4062,0 +4063,0 +4064,0 +4065,3 +4066,0 +4067,0 +4068,1 +4069,3 +4070,0 +4071,2 +4072,3 +4073,0 +4074,1 +4075,0 +4076,0 +4077,0 +4078,0 +4079,2 +4080,3 +4081,1 +4082,0 +4083,0 +4084,0 +4085,3 +4086,2 +4087,3 +4088,0 +4089,0 +4090,1 +4091,0 +4092,0 +4093,0 +4094,2 +4095,1 +4096,0 +4097,0 +4098,0 +4099,3 +4100,1 +4101,0 +4102,2 +4103,2 +4104,2 +4105,3 +4106,0 +4107,2 +4108,0 +4109,0 +4110,0 +4111,0 +4112,2 +4113,0 +4114,0 +4115,3 +4116,3 +4117,3 +4118,0 +4119,1 +4120,2 +4121,0 +4122,0 +4123,2 +4124,0 +4125,2 +4126,2 +4127,3 +4128,0 +4129,3 +4130,0 +4131,3 +4132,2 +4133,0 +4134,0 +4135,2 +4136,0 +4137,2 +4138,2 +4139,1 +4140,1 +4141,3 +4142,3 +4143,2 +4144,3 +4145,0 +4146,2 +4147,0 +4148,0 +4149,3 +4150,0 +4151,0 +4152,0 +4153,2 +4154,1 +4155,0 +4156,0 +4157,3 +4158,1 +4159,2 +4160,3 +4161,3 +4162,2 +4163,2 +4164,0 +4165,3 +4166,0 +4167,3 +4168,2 +4169,0 +4170,2 +4171,0 +4172,2 +4173,0 +4174,0 +4175,3 +4176,1 +4177,0 +4178,2 +4179,2 +4180,0 +4181,1 +4182,2 +4183,2 +4184,0 +4185,0 +4186,2 +4187,3 +4188,0 +4189,2 +4190,2 +4191,3 +4192,0 +4193,3 +4194,2 +4195,2 +4196,0 +4197,0 +4198,1 +4199,2 +4200,3 +4201,0 +4202,0 +4203,2 +4204,0 +4205,0 +4206,3 +4207,0 +4208,0 +4209,0 +4210,0 +4211,0 +4212,0 +4213,0 +4214,1 +4215,0 +4216,1 +4217,0 +4218,0 +4219,3 +4220,0 +4221,3 +4222,2 +4223,0 +4224,0 +4225,0 +4226,0 +4227,1 +4228,3 +4229,0 +4230,0 +4231,0 +4232,2 +4233,2 +4234,0 +4235,2 +4236,1 +4237,0 +4238,3 +4239,0 +4240,2 +4241,3 +4242,0 +4243,0 +4244,0 +4245,3 +4246,0 +4247,1 +4248,3 +4249,0 +4250,3 +4251,0 +4252,0 +4253,2 +4254,0 +4255,0 +4256,3 +4257,0 +4258,1 +4259,3 +4260,3 +4261,0 +4262,3 +4263,1 +4264,2 +4265,0 +4266,2 +4267,2 +4268,0 +4269,0 +4270,2 +4271,1 +4272,3 +4273,0 +4274,1 +4275,3 +4276,0 +4277,2 +4278,1 +4279,0 +4280,0 +4281,0 +4282,2 +4283,2 +4284,0 +4285,0 +4286,1 +4287,0 +4288,1 +4289,2 +4290,3 +4291,0 +4292,2 +4293,0 +4294,0 +4295,1 +4296,1 +4297,1 +4298,2 +4299,1 +4300,0 +4301,3 +4302,2 +4303,0 +4304,0 +4305,1 +4306,3 +4307,3 +4308,3 +4309,2 +4310,1 +4311,2 +4312,0 +4313,3 +4314,2 +4315,3 +4316,2 +4317,3 +4318,0 +4319,0 +4320,3 +4321,3 +4322,1 +4323,0 +4324,0 +4325,3 +4326,1 +4327,3 +4328,1 +4329,1 +4330,1 +4331,1 +4332,0 +4333,3 +4334,2 +4335,2 +4336,0 +4337,2 +4338,0 +4339,3 +4340,2 +4341,3 +4342,3 +4343,3 +4344,3 +4345,2 +4346,0 +4347,2 +4348,3 +4349,2 +4350,0 +4351,2 +4352,3 +4353,1 +4354,0 +4355,0 +4356,0 +4357,0 +4358,1 +4359,0 +4360,2 +4361,0 +4362,0 +4363,0 +4364,2 +4365,2 +4366,2 +4367,0 +4368,2 +4369,2 +4370,0 +4371,0 +4372,0 +4373,3 +4374,0 +4375,0 +4376,1 +4377,0 +4378,3 +4379,0 +4380,1 +4381,0 +4382,0 +4383,3 +4384,0 +4385,3 +4386,1 +4387,0 +4388,0 +4389,0 +4390,2 +4391,2 +4392,0 +4393,0 +4394,0 +4395,1 +4396,1 +4397,0 +4398,2 +4399,0 +4400,2 +4401,3 +4402,2 +4403,0 +4404,1 +4405,1 +4406,2 +4407,0 +4408,0 +4409,0 +4410,2 +4411,1 +4412,0 +4413,0 +4414,3 +4415,0 +4416,0 +4417,3 +4418,3 +4419,2 +4420,1 +4421,2 +4422,1 +4423,0 +4424,3 +4425,0 +4426,0 +4427,2 +4428,0 +4429,0 +4430,3 +4431,2 +4432,1 +4433,2 +4434,1 +4435,2 +4436,0 +4437,0 +4438,0 +4439,0 +4440,0 +4441,2 +4442,3 +4443,2 +4444,3 +4445,0 +4446,1 +4447,1 +4448,2 +4449,0 +4450,0 +4451,0 +4452,1 +4453,3 +4454,1 +4455,0 +4456,2 +4457,0 +4458,1 +4459,3 +4460,2 +4461,2 +4462,0 +4463,3 +4464,3 +4465,0 +4466,0 +4467,2 +4468,0 +4469,1 +4470,0 +4471,0 +4472,1 +4473,1 +4474,0 +4475,1 +4476,0 +4477,2 +4478,0 +4479,0 +4480,3 +4481,3 +4482,1 +4483,3 +4484,3 +4485,3 +4486,0 +4487,0 +4488,1 +4489,3 +4490,3 +4491,2 +4492,3 +4493,0 +4494,1 +4495,0 +4496,3 +4497,3 +4498,0 +4499,0 +4500,0 +4501,0 +4502,1 +4503,0 +4504,2 +4505,3 +4506,0 +4507,3 +4508,1 +4509,0 +4510,3 +4511,0 +4512,0 +4513,3 +4514,3 +4515,3 +4516,2 +4517,2 +4518,1 +4519,0 +4520,0 +4521,2 +4522,2 +4523,3 +4524,2 +4525,0 +4526,3 +4527,0 +4528,1 +4529,1 +4530,2 +4531,2 +4532,1 +4533,0 +4534,2 +4535,0 +4536,3 +4537,3 +4538,2 +4539,2 +4540,1 +4541,2 +4542,2 +4543,2 +4544,3 +4545,3 +4546,3 +4547,3 +4548,2 +4549,0 +4550,2 +4551,1 +4552,3 +4553,0 +4554,2 +4555,2 +4556,3 +4557,0 +4558,3 +4559,3 +4560,2 +4561,1 +4562,3 +4563,0 +4564,0 +4565,0 +4566,0 +4567,1 +4568,3 +4569,3 +4570,0 +4571,2 +4572,0 +4573,0 +4574,2 +4575,3 +4576,3 +4577,1 +4578,0 +4579,3 +4580,2 +4581,0 +4582,0 +4583,2 +4584,2 +4585,1 +4586,0 +4587,2 +4588,2 +4589,0 +4590,2 +4591,0 +4592,0 +4593,0 +4594,2 +4595,1 +4596,0 +4597,0 +4598,2 +4599,0 +4600,3 +4601,3 +4602,3 +4603,3 +4604,0 +4605,2 +4606,0 +4607,1 +4608,0 +4609,2 +4610,0 +4611,0 +4612,0 +4613,3 +4614,0 +4615,3 +4616,3 +4617,2 +4618,0 +4619,2 +4620,0 +4621,2 +4622,3 +4623,0 +4624,3 +4625,0 +4626,3 +4627,0 +4628,0 +4629,3 +4630,0 +4631,0 +4632,0 +4633,3 +4634,0 +4635,0 +4636,1 +4637,2 +4638,0 +4639,0 +4640,0 +4641,0 +4642,1 +4643,0 +4644,0 +4645,1 +4646,0 +4647,0 +4648,0 +4649,0 +4650,3 +4651,0 +4652,0 +4653,3 +4654,0 +4655,0 +4656,3 +4657,2 +4658,0 +4659,3 +4660,1 +4661,1 +4662,1 +4663,0 +4664,1 +4665,1 +4666,2 +4667,2 +4668,1 +4669,3 +4670,0 +4671,0 +4672,3 +4673,2 +4674,0 +4675,0 +4676,3 +4677,1 +4678,0 +4679,1 +4680,2 +4681,0 +4682,3 +4683,3 +4684,0 +4685,2 +4686,0 +4687,1 +4688,0 +4689,0 +4690,2 +4691,0 +4692,0 +4693,3 +4694,1 +4695,1 +4696,2 +4697,1 +4698,0 +4699,0 +4700,0 +4701,0 +4702,2 +4703,0 +4704,1 +4705,0 +4706,3 +4707,3 +4708,2 +4709,1 +4710,0 +4711,0 +4712,3 +4713,3 +4714,0 +4715,0 +4716,0 +4717,0 +4718,3 +4719,1 +4720,1 +4721,3 +4722,2 +4723,2 +4724,3 +4725,0 +4726,2 +4727,0 +4728,3 +4729,1 +4730,0 +4731,0 +4732,3 +4733,3 +4734,0 +4735,0 +4736,0 +4737,0 +4738,0 +4739,2 +4740,3 +4741,0 +4742,2 +4743,2 +4744,0 +4745,3 +4746,0 +4747,0 +4748,2 +4749,0 +4750,2 +4751,3 +4752,1 +4753,1 +4754,0 +4755,2 +4756,1 +4757,2 +4758,0 +4759,1 +4760,3 +4761,3 +4762,2 +4763,0 +4764,0 +4765,2 +4766,3 +4767,2 +4768,0 +4769,3 +4770,0 +4771,0 +4772,2 +4773,1 +4774,3 +4775,3 +4776,2 +4777,0 +4778,0 +4779,3 +4780,1 +4781,0 +4782,2 +4783,0 +4784,0 +4785,1 +4786,0 +4787,0 +4788,1 +4789,0 +4790,0 +4791,0 +4792,0 +4793,0 +4794,3 +4795,2 +4796,0 +4797,2 +4798,1 +4799,0 +4800,0 +4801,0 +4802,2 +4803,3 +4804,0 +4805,0 +4806,0 +4807,0 +4808,3 +4809,0 +4810,1 +4811,3 +4812,0 +4813,2 +4814,2 +4815,0 +4816,0 +4817,0 +4818,0 +4819,3 +4820,0 +4821,0 +4822,2 +4823,0 +4824,0 +4825,0 +4826,1 +4827,3 +4828,2 +4829,3 +4830,3 +4831,0 +4832,0 +4833,2 +4834,3 +4835,1 +4836,1 +4837,2 +4838,0 +4839,3 +4840,3 +4841,2 +4842,3 +4843,0 +4844,0 +4845,3 +4846,0 +4847,0 +4848,3 +4849,0 +4850,0 +4851,0 +4852,1 +4853,2 +4854,1 +4855,2 +4856,1 +4857,0 +4858,0 +4859,0 +4860,3 +4861,2 +4862,0 +4863,3 +4864,0 +4865,0 +4866,1 +4867,2 +4868,1 +4869,3 +4870,1 +4871,1 +4872,0 +4873,0 +4874,3 +4875,3 +4876,3 +4877,3 +4878,1 +4879,1 +4880,0 +4881,1 +4882,3 +4883,2 +4884,1 +4885,0 +4886,0 +4887,1 +4888,0 +4889,0 +4890,2 +4891,3 +4892,3 +4893,2 +4894,0 +4895,2 +4896,1 +4897,0 +4898,1 +4899,3 +4900,2 +4901,0 +4902,0 +4903,0 +4904,1 +4905,2 +4906,3 +4907,0 +4908,0 +4909,0 +4910,3 +4911,1 +4912,2 +4913,3 +4914,1 +4915,2 +4916,3 +4917,3 +4918,3 +4919,3 +4920,0 +4921,0 +4922,2 +4923,0 +4924,3 +4925,1 +4926,2 +4927,0 +4928,0 +4929,2 +4930,0 +4931,0 +4932,3 +4933,3 +4934,1 +4935,1 +4936,3 +4937,3 +4938,1 +4939,0 +4940,0 +4941,3 +4942,2 +4943,0 +4944,3 +4945,2 +4946,0 +4947,0 +4948,3 +4949,0 +4950,0 +4951,3 +4952,0 +4953,0 +4954,0 +4955,3 +4956,3 +4957,1 +4958,0 +4959,1 +4960,1 +4961,0 +4962,0 +4963,0 +4964,2 +4965,0 +4966,0 +4967,0 +4968,1 +4969,0 +4970,0 +4971,0 +4972,1 +4973,2 +4974,0 +4975,0 +4976,0 +4977,0 +4978,3 +4979,2 +4980,0 +4981,0 +4982,3 +4983,0 +4984,0 +4985,3 +4986,2 +4987,3 +4988,0 +4989,2 +4990,0 +4991,0 +4992,0 +4993,0 +4994,0 +4995,0 +4996,3 +4997,0 +4998,2 +4999,3 +5000,0 +5001,0 +5002,0 +5003,1 +5004,3 +5005,0 +5006,0 +5007,0 +5008,0 +5009,0 +5010,2 +5011,1 +5012,0 +5013,2 +5014,3 +5015,2 +5016,0 +5017,0 +5018,0 +5019,0 +5020,3 +5021,3 +5022,0 +5023,0 +5024,2 +5025,3 +5026,0 +5027,3 +5028,3 +5029,3 +5030,0 +5031,0 +5032,3 +5033,0 +5034,0 +5035,1 +5036,0 +5037,1 +5038,3 +5039,0 +5040,0 +5041,3 +5042,2 +5043,3 +5044,1 +5045,2 +5046,0 +5047,0 +5048,0 +5049,0 +5050,0 +5051,2 +5052,0 +5053,1 +5054,0 +5055,0 +5056,0 +5057,3 +5058,1 +5059,2 +5060,2 +5061,2 +5062,2 +5063,1 +5064,1 +5065,2 +5066,3 +5067,0 +5068,2 +5069,0 +5070,3 +5071,0 +5072,0 +5073,0 +5074,0 +5075,1 +5076,1 +5077,0 +5078,2 +5079,2 +5080,0 +5081,3 +5082,0 +5083,0 +5084,0 +5085,2 +5086,0 +5087,0 +5088,0 +5089,3 +5090,0 +5091,0 +5092,0 +5093,2 +5094,0 +5095,0 +5096,1 +5097,0 +5098,0 +5099,1 +5100,3 +5101,3 +5102,0 +5103,0 +5104,0 +5105,3 +5106,0 +5107,2 +5108,3 +5109,0 +5110,0 +5111,3 +5112,2 +5113,1 +5114,3 +5115,2 +5116,1 +5117,0 +5118,0 +5119,2 +5120,2 +5121,2 +5122,1 +5123,0 +5124,0 +5125,0 +5126,0 +5127,2 +5128,0 +5129,0 +5130,0 +5131,1 +5132,0 +5133,1 +5134,0 +5135,0 +5136,1 +5137,0 +5138,0 +5139,0 +5140,3 +5141,0 +5142,2 +5143,2 +5144,2 +5145,1 +5146,3 +5147,1 +5148,0 +5149,0 +5150,0 +5151,2 +5152,2 +5153,0 +5154,0 +5155,0 +5156,0 +5157,2 +5158,3 +5159,0 +5160,1 +5161,2 +5162,2 +5163,0 +5164,0 +5165,0 +5166,0 +5167,0 +5168,0 +5169,0 +5170,0 +5171,0 +5172,3 +5173,0 +5174,0 +5175,1 +5176,0 +5177,3 +5178,2 +5179,1 +5180,3 +5181,3 +5182,2 +5183,1 +5184,1 +5185,0 +5186,0 +5187,0 +5188,3 +5189,2 +5190,0 +5191,0 +5192,0 +5193,1 +5194,0 +5195,0 +5196,2 +5197,2 +5198,0 +5199,3 +5200,3 +5201,0 +5202,0 +5203,3 +5204,3 +5205,1 +5206,2 +5207,3 +5208,0 +5209,1 +5210,3 +5211,0 +5212,2 +5213,0 +5214,0 +5215,0 +5216,3 +5217,3 +5218,2 +5219,2 +5220,0 +5221,2 +5222,1 +5223,0 +5224,1 +5225,3 +5226,0 +5227,0 +5228,2 +5229,0 +5230,3 +5231,0 +5232,2 +5233,3 +5234,1 +5235,1 +5236,0 +5237,0 +5238,2 +5239,0 +5240,0 +5241,0 +5242,2 +5243,3 +5244,0 +5245,3 +5246,3 +5247,2 +5248,0 +5249,0 +5250,3 +5251,0 +5252,3 +5253,0 +5254,2 +5255,0 +5256,2 +5257,0 +5258,0 +5259,3 +5260,3 +5261,2 +5262,0 +5263,0 +5264,1 +5265,3 +5266,0 +5267,0 +5268,0 +5269,1 +5270,3 +5271,2 +5272,3 +5273,0 +5274,3 +5275,2 +5276,0 +5277,0 +5278,3 +5279,2 +5280,3 +5281,0 +5282,1 +5283,0 +5284,3 +5285,0 +5286,0 +5287,0 +5288,1 +5289,2 +5290,1 +5291,1 +5292,0 +5293,0 +5294,2 +5295,2 +5296,0 +5297,0 +5298,0 +5299,3 +5300,0 +5301,0 +5302,0 +5303,0 +5304,0 +5305,2 +5306,1 +5307,3 +5308,0 +5309,0 +5310,3 +5311,2 +5312,0 +5313,0 +5314,0 +5315,3 +5316,2 +5317,1 +5318,3 +5319,3 +5320,2 +5321,0 +5322,0 +5323,0 +5324,3 +5325,2 +5326,0 +5327,0 +5328,1 +5329,1 +5330,0 +5331,0 +5332,0 +5333,2 +5334,3 +5335,2 +5336,0 +5337,0 +5338,1 +5339,2 +5340,2 +5341,0 +5342,0 +5343,3 +5344,0 +5345,1 +5346,1 +5347,0 +5348,1 +5349,0 +5350,0 +5351,1 +5352,0 +5353,3 +5354,3 +5355,3 +5356,3 +5357,2 +5358,3 +5359,0 +5360,0 +5361,3 +5362,1 +5363,1 +5364,0 +5365,3 +5366,2 +5367,0 +5368,3 +5369,3 +5370,3 +5371,2 +5372,2 +5373,0 +5374,3 +5375,0 +5376,3 +5377,2 +5378,0 +5379,0 +5380,0 +5381,0 +5382,0 +5383,0 +5384,0 +5385,0 +5386,0 +5387,0 +5388,2 +5389,3 +5390,0 +5391,1 +5392,3 +5393,1 +5394,0 +5395,1 +5396,3 +5397,0 +5398,2 +5399,0 +5400,3 +5401,3 +5402,2 +5403,1 +5404,0 +5405,0 +5406,0 +5407,0 +5408,3 +5409,2 +5410,3 +5411,1 +5412,3 +5413,3 +5414,0 +5415,2 +5416,2 +5417,3 +5418,3 +5419,0 +5420,2 +5421,1 +5422,1 +5423,3 +5424,0 +5425,1 +5426,3 +5427,0 +5428,0 +5429,0 +5430,1 +5431,2 +5432,0 +5433,1 +5434,0 +5435,3 +5436,0 +5437,0 +5438,1 +5439,3 +5440,1 +5441,3 +5442,3 +5443,3 +5444,1 +5445,0 +5446,2 +5447,0 +5448,0 +5449,1 +5450,0 +5451,2 +5452,3 +5453,1 +5454,0 +5455,0 +5456,0 +5457,0 +5458,0 +5459,0 +5460,2 +5461,0 +5462,1 +5463,0 +5464,0 +5465,0 +5466,0 +5467,0 +5468,1 +5469,0 +5470,0 +5471,0 +5472,0 +5473,0 +5474,0 +5475,1 +5476,3 +5477,2 +5478,0 +5479,0 +5480,0 +5481,2 +5482,2 +5483,1 +5484,0 +5485,0 +5486,1 +5487,0 +5488,0 +5489,3 +5490,0 +5491,0 +5492,2 +5493,2 +5494,1 +5495,0 +5496,0 +5497,0 +5498,0 +5499,3 +5500,2 +5501,0 +5502,0 +5503,0 +5504,3 +5505,3 +5506,3 +5507,0 +5508,3 +5509,3 +5510,3 +5511,2 +5512,3 +5513,0 +5514,3 +5515,3 +5516,3 +5517,3 +5518,2 +5519,0 +5520,0 +5521,3 +5522,0 +5523,3 +5524,3 +5525,3 +5526,3 +5527,3 +5528,1 +5529,0 +5530,2 +5531,0 +5532,0 +5533,3 +5534,0 +5535,1 +5536,2 +5537,3 +5538,0 +5539,0 +5540,2 +5541,2 +5542,0 +5543,1 +5544,2 +5545,0 +5546,0 +5547,3 +5548,3 +5549,0 +5550,3 +5551,0 +5552,1 +5553,0 +5554,0 +5555,0 +5556,1 +5557,0 +5558,0 +5559,0 +5560,0 +5561,1 +5562,0 +5563,3 +5564,2 +5565,1 +5566,3 +5567,2 +5568,0 +5569,2 +5570,0 +5571,0 +5572,0 +5573,2 +5574,0 +5575,2 +5576,1 +5577,0 +5578,3 +5579,0 +5580,0 +5581,0 +5582,0 +5583,3 +5584,1 +5585,3 +5586,1 +5587,0 +5588,0 +5589,1 +5590,3 +5591,3 +5592,2 +5593,2 +5594,2 +5595,2 +5596,1 +5597,0 +5598,0 +5599,1 +5600,0 +5601,1 +5602,2 +5603,3 +5604,3 +5605,2 +5606,3 +5607,3 +5608,2 +5609,0 +5610,2 +5611,3 +5612,1 +5613,2 +5614,3 +5615,0 +5616,0 +5617,0 +5618,1 +5619,1 +5620,3 +5621,0 +5622,3 +5623,0 +5624,0 +5625,0 +5626,3 +5627,3 +5628,3 +5629,2 +5630,2 +5631,2 +5632,0 +5633,3 +5634,2 +5635,0 +5636,0 +5637,0 +5638,2 +5639,0 +5640,0 +5641,2 +5642,0 +5643,0 +5644,0 +5645,3 +5646,2 +5647,2 +5648,1 +5649,3 +5650,2 +5651,1 +5652,0 +5653,0 +5654,0 +5655,1 +5656,3 +5657,0 +5658,3 +5659,0 +5660,0 +5661,0 +5662,3 +5663,2 +5664,3 +5665,0 +5666,2 +5667,1 +5668,2 +5669,0 +5670,2 +5671,0 +5672,0 +5673,3 +5674,1 +5675,2 +5676,3 +5677,3 +5678,1 +5679,3 +5680,0 +5681,1 +5682,0 +5683,0 +5684,0 +5685,0 +5686,1 +5687,3 +5688,0 +5689,1 +5690,0 +5691,0 +5692,0 +5693,2 +5694,0 +5695,0 +5696,0 +5697,2 +5698,3 +5699,0 +5700,2 +5701,3 +5702,2 +5703,3 +5704,0 +5705,0 +5706,0 +5707,0 +5708,0 +5709,0 +5710,3 +5711,2 +5712,0 +5713,0 +5714,0 +5715,0 +5716,2 +5717,0 +5718,2 +5719,0 +5720,0 +5721,0 +5722,2 +5723,1 +5724,3 +5725,2 +5726,0 +5727,3 +5728,3 +5729,0 +5730,0 +5731,0 +5732,0 +5733,0 +5734,1 +5735,0 +5736,3 +5737,3 +5738,0 +5739,0 +5740,0 +5741,0 +5742,3 +5743,0 +5744,1 +5745,0 +5746,1 +5747,3 +5748,1 +5749,0 +5750,0 +5751,1 +5752,0 +5753,0 +5754,3 +5755,0 +5756,0 +5757,3 +5758,0 +5759,0 +5760,0 +5761,2 +5762,3 +5763,1 +5764,1 +5765,0 +5766,0 +5767,2 +5768,3 +5769,0 +5770,2 +5771,0 +5772,2 +5773,1 +5774,0 +5775,0 +5776,1 +5777,0 +5778,0 +5779,3 +5780,2 +5781,0 +5782,3 +5783,2 +5784,3 +5785,0 +5786,1 +5787,2 +5788,3 +5789,0 +5790,0 +5791,2 +5792,0 +5793,2 +5794,2 +5795,1 +5796,0 +5797,3 +5798,2 +5799,0 +5800,0 +5801,2 +5802,3 +5803,3 +5804,1 +5805,3 +5806,3 +5807,3 +5808,1 +5809,1 +5810,0 +5811,0 +5812,0 +5813,0 +5814,1 +5815,0 +5816,1 +5817,3 +5818,3 +5819,3 +5820,0 +5821,1 +5822,3 +5823,1 +5824,3 +5825,0 +5826,1 +5827,0 +5828,0 +5829,0 +5830,0 +5831,3 +5832,0 +5833,2 +5834,0 +5835,2 +5836,3 +5837,2 +5838,2 +5839,0 +5840,2 +5841,2 +5842,1 +5843,0 +5844,1 +5845,2 +5846,0 +5847,2 +5848,1 +5849,0 +5850,1 +5851,1 +5852,2 +5853,2 +5854,0 +5855,3 +5856,0 +5857,2 +5858,0 +5859,1 +5860,3 +5861,3 +5862,3 +5863,3 +5864,1 +5865,2 +5866,0 +5867,2 +5868,0 +5869,0 +5870,2 +5871,1 +5872,1 +5873,0 +5874,0 +5875,0 +5876,2 +5877,2 +5878,2 +5879,0 +5880,1 +5881,1 +5882,1 +5883,0 +5884,2 +5885,0 +5886,3 +5887,0 +5888,0 +5889,0 +5890,2 +5891,1 +5892,0 +5893,0 +5894,3 +5895,1 +5896,0 +5897,0 +5898,2 +5899,0 +5900,0 +5901,3 +5902,1 +5903,2 +5904,0 +5905,3 +5906,0 +5907,3 +5908,0 +5909,3 +5910,0 +5911,3 +5912,3 +5913,2 +5914,0 +5915,0 +5916,0 +5917,3 +5918,2 +5919,0 +5920,1 +5921,3 +5922,0 +5923,0 +5924,3 +5925,0 +5926,2 +5927,2 +5928,0 +5929,0 +5930,0 +5931,2 +5932,0 +5933,0 +5934,3 +5935,0 +5936,0 +5937,2 +5938,0 +5939,2 +5940,1 +5941,1 +5942,1 +5943,3 +5944,0 +5945,1 +5946,0 +5947,2 +5948,3 +5949,0 +5950,2 +5951,2 +5952,1 +5953,2 +5954,0 +5955,0 +5956,3 +5957,1 +5958,0 +5959,3 +5960,3 +5961,0 +5962,2 +5963,0 +5964,0 +5965,0 +5966,3 +5967,0 +5968,3 +5969,0 +5970,3 +5971,0 +5972,2 +5973,1 +5974,1 +5975,0 +5976,0 +5977,1 +5978,2 +5979,3 +5980,2 +5981,1 +5982,0 +5983,3 +5984,3 +5985,2 +5986,3 +5987,1 +5988,2 +5989,0 +5990,0 +5991,1 +5992,3 +5993,0 +5994,0 +5995,0 +5996,2 +5997,2 +5998,0 +5999,3 +6000,0 +6001,3 +6002,1 +6003,0 +6004,3 +6005,1 +6006,3 +6007,0 +6008,0 +6009,2 +6010,3 +6011,2 +6012,0 +6013,3 +6014,0 +6015,3 +6016,3 +6017,3 +6018,0 +6019,2 +6020,0 +6021,0 +6022,2 +6023,3 +6024,3 +6025,1 +6026,0 +6027,0 +6028,0 +6029,0 +6030,3 +6031,0 +6032,0 +6033,3 +6034,0 +6035,2 +6036,0 +6037,0 +6038,2 +6039,0 +6040,2 +6041,2 +6042,2 +6043,1 +6044,0 +6045,1 +6046,2 +6047,1 +6048,1 +6049,0 +6050,1 +6051,0 +6052,3 +6053,0 +6054,0 +6055,0 +6056,3 +6057,1 +6058,2 +6059,2 +6060,0 +6061,0 +6062,3 +6063,3 +6064,3 +6065,2 +6066,0 +6067,1 +6068,2 +6069,0 +6070,0 +6071,2 +6072,0 +6073,2 +6074,2 +6075,1 +6076,0 +6077,1 +6078,0 +6079,2 +6080,3 +6081,2 +6082,0 +6083,0 +6084,0 +6085,2 +6086,2 +6087,1 +6088,1 +6089,3 +6090,3 +6091,0 +6092,0 +6093,0 +6094,1 +6095,3 +6096,0 +6097,3 +6098,1 +6099,2 +6100,0 +6101,2 +6102,0 +6103,2 +6104,0 +6105,0 +6106,0 +6107,0 +6108,0 +6109,0 +6110,2 +6111,1 +6112,0 +6113,0 +6114,2 +6115,3 +6116,0 +6117,0 +6118,0 +6119,0 +6120,0 +6121,2 +6122,3 +6123,0 +6124,3 +6125,2 +6126,0 +6127,2 +6128,0 +6129,0 +6130,1 +6131,0 +6132,0 +6133,0 +6134,3 +6135,0 +6136,0 +6137,3 +6138,0 +6139,2 +6140,2 +6141,0 +6142,2 +6143,2 +6144,3 +6145,3 +6146,0 +6147,1 +6148,0 +6149,0 +6150,0 +6151,2 +6152,0 +6153,2 +6154,0 +6155,3 +6156,1 +6157,3 +6158,2 +6159,0 +6160,0 +6161,0 +6162,0 +6163,0 +6164,3 +6165,0 +6166,1 +6167,0 +6168,2 +6169,0 +6170,0 +6171,2 +6172,0 +6173,2 +6174,0 +6175,3 +6176,0 +6177,2 +6178,2 +6179,2 +6180,1 +6181,3 +6182,3 +6183,0 +6184,0 +6185,2 +6186,0 +6187,0 +6188,3 +6189,0 +6190,0 +6191,3 +6192,2 +6193,0 +6194,2 +6195,3 +6196,0 +6197,2 +6198,0 +6199,0 +6200,0 +6201,1 +6202,2 +6203,0 +6204,0 +6205,0 +6206,0 +6207,1 +6208,3 +6209,2 +6210,2 +6211,1 +6212,3 +6213,2 +6214,0 +6215,3 +6216,3 +6217,1 +6218,1 +6219,0 +6220,0 +6221,0 +6222,0 +6223,0 +6224,1 +6225,0 +6226,1 +6227,3 +6228,1 +6229,0 +6230,0 +6231,0 +6232,0 +6233,3 +6234,2 +6235,3 +6236,0 +6237,0 +6238,0 +6239,0 +6240,0 +6241,0 +6242,0 +6243,1 +6244,0 +6245,2 +6246,2 +6247,0 +6248,0 +6249,0 +6250,3 +6251,1 +6252,1 +6253,0 +6254,0 +6255,0 +6256,3 +6257,0 +6258,0 +6259,3 +6260,0 +6261,0 +6262,2 +6263,2 +6264,3 +6265,3 +6266,3 +6267,0 +6268,0 +6269,3 +6270,0 +6271,2 +6272,2 +6273,0 +6274,3 +6275,2 +6276,2 +6277,0 +6278,0 +6279,1 +6280,1 +6281,2 +6282,0 +6283,0 +6284,1 +6285,0 +6286,0 +6287,3 +6288,2 +6289,1 +6290,3 +6291,3 +6292,0 +6293,0 +6294,3 +6295,0 +6296,1 +6297,0 +6298,0 +6299,2 +6300,3 +6301,3 +6302,2 +6303,0 +6304,2 +6305,0 +6306,0 +6307,0 +6308,0 +6309,2 +6310,1 +6311,3 +6312,2 +6313,0 +6314,3 +6315,3 +6316,3 +6317,2 +6318,3 +6319,3 +6320,3 +6321,3 +6322,3 +6323,2 +6324,2 +6325,0 +6326,3 +6327,0 +6328,2 +6329,0 +6330,0 +6331,0 +6332,3 +6333,0 +6334,3 +6335,0 +6336,3 +6337,0 +6338,0 +6339,0 +6340,3 +6341,0 +6342,0 +6343,2 +6344,0 +6345,1 +6346,1 +6347,3 +6348,2 +6349,1 +6350,0 +6351,0 +6352,3 +6353,2 +6354,0 +6355,0 +6356,0 +6357,3 +6358,3 +6359,2 +6360,0 +6361,0 +6362,3 +6363,2 +6364,3 +6365,3 +6366,2 +6367,1 +6368,0 +6369,3 +6370,2 +6371,3 +6372,0 +6373,3 +6374,0 +6375,0 +6376,2 +6377,0 +6378,0 +6379,0 +6380,2 +6381,1 +6382,3 +6383,2 +6384,0 +6385,3 +6386,2 +6387,0 +6388,0 +6389,3 +6390,3 +6391,3 +6392,3 +6393,0 +6394,3 +6395,0 +6396,3 +6397,0 +6398,0 +6399,1 +6400,1 +6401,0 +6402,1 +6403,0 +6404,3 +6405,3 +6406,2 +6407,0 +6408,2 +6409,3 +6410,1 +6411,2 +6412,2 +6413,0 +6414,0 +6415,0 +6416,2 +6417,0 +6418,0 +6419,1 +6420,0 +6421,2 +6422,3 +6423,2 +6424,0 +6425,2 +6426,1 +6427,0 +6428,0 +6429,0 +6430,2 +6431,0 +6432,2 +6433,1 +6434,2 +6435,1 +6436,1 +6437,2 +6438,3 +6439,1 +6440,3 +6441,2 +6442,2 +6443,0 +6444,1 +6445,0 +6446,3 +6447,0 +6448,0 +6449,2 +6450,3 +6451,0 +6452,3 +6453,0 +6454,3 +6455,0 +6456,0 +6457,0 +6458,3 +6459,2 +6460,0 +6461,3 +6462,2 +6463,0 +6464,2 +6465,0 +6466,0 +6467,1 +6468,1 +6469,1 +6470,0 +6471,0 +6472,0 +6473,2 +6474,0 +6475,1 +6476,2 +6477,3 +6478,0 +6479,2 +6480,3 +6481,3 +6482,2 +6483,2 +6484,2 +6485,3 +6486,0 +6487,1 +6488,0 +6489,0 +6490,0 +6491,0 +6492,0 +6493,2 +6494,3 +6495,0 +6496,2 +6497,0 +6498,2 +6499,3 +6500,3 +6501,1 +6502,0 +6503,3 +6504,0 +6505,0 +6506,3 +6507,0 +6508,3 +6509,3 +6510,0 +6511,2 +6512,3 +6513,3 +6514,1 +6515,1 +6516,2 +6517,1 +6518,2 +6519,0 +6520,2 +6521,3 +6522,0 +6523,0 +6524,1 +6525,0 +6526,3 +6527,3 +6528,0 +6529,1 +6530,1 +6531,0 +6532,0 +6533,0 +6534,0 +6535,0 +6536,3 +6537,2 +6538,2 +6539,2 +6540,0 +6541,0 +6542,0 +6543,0 +6544,0 +6545,1 +6546,3 +6547,0 +6548,0 +6549,1 +6550,1 +6551,0 +6552,0 +6553,2 +6554,2 +6555,0 +6556,2 +6557,2 +6558,1 +6559,3 +6560,2 +6561,1 +6562,1 +6563,2 +6564,0 +6565,0 +6566,3 +6567,3 +6568,0 +6569,2 +6570,1 +6571,2 +6572,0 +6573,0 +6574,0 +6575,3 +6576,0 +6577,3 +6578,0 +6579,0 +6580,1 +6581,0 +6582,3 +6583,0 +6584,1 +6585,0 +6586,0 +6587,0 +6588,3 +6589,0 +6590,0 +6591,0 +6592,0 +6593,3 +6594,0 +6595,0 +6596,0 +6597,2 +6598,2 +6599,3 +6600,3 +6601,0 +6602,0 +6603,0 +6604,3 +6605,3 +6606,3 +6607,2 +6608,1 +6609,0 +6610,0 +6611,1 +6612,2 +6613,3 +6614,2 +6615,0 +6616,2 +6617,3 +6618,2 +6619,0 +6620,0 +6621,0 +6622,3 +6623,3 +6624,0 +6625,1 +6626,0 +6627,3 +6628,0 +6629,0 +6630,3 +6631,3 +6632,2 +6633,2 +6634,0 +6635,1 +6636,0 +6637,2 +6638,2 +6639,0 +6640,3 +6641,1 +6642,0 +6643,3 +6644,2 +6645,0 +6646,0 +6647,0 +6648,3 +6649,3 +6650,0 +6651,0 +6652,0 +6653,3 +6654,3 +6655,0 +6656,0 +6657,0 +6658,2 +6659,1 +6660,3 +6661,3 +6662,2 +6663,0 +6664,0 +6665,0 +6666,0 +6667,0 +6668,0 +6669,3 +6670,0 +6671,3 +6672,0 +6673,0 +6674,0 +6675,1 +6676,2 +6677,0 +6678,2 +6679,2 +6680,0 +6681,0 +6682,0 +6683,0 +6684,3 +6685,2 +6686,2 +6687,1 +6688,2 +6689,0 +6690,0 +6691,2 +6692,3 +6693,1 +6694,3 +6695,0 +6696,2 +6697,0 +6698,3 +6699,0 +6700,2 +6701,0 +6702,3 +6703,1 +6704,0 +6705,2 +6706,1 +6707,2 +6708,1 +6709,3 +6710,0 +6711,2 +6712,0 +6713,2 +6714,0 +6715,0 +6716,3 +6717,3 +6718,2 +6719,2 +6720,2 +6721,0 +6722,0 +6723,0 +6724,0 +6725,0 +6726,0 +6727,1 +6728,1 +6729,0 +6730,0 +6731,1 +6732,2 +6733,0 +6734,0 +6735,2 +6736,2 +6737,0 +6738,0 +6739,0 +6740,0 +6741,1 +6742,1 +6743,1 +6744,0 +6745,3 +6746,3 +6747,0 +6748,1 +6749,0 +6750,0 +6751,3 +6752,0 +6753,0 +6754,3 +6755,0 +6756,3 +6757,2 +6758,1 +6759,3 +6760,0 +6761,3 +6762,2 +6763,0 +6764,3 +6765,0 +6766,3 +6767,3 +6768,0 +6769,3 +6770,2 +6771,2 +6772,2 +6773,2 +6774,0 +6775,3 +6776,0 +6777,0 +6778,1 +6779,2 +6780,3 +6781,3 +6782,2 +6783,1 +6784,2 +6785,0 +6786,3 +6787,3 +6788,1 +6789,0 +6790,2 +6791,0 +6792,2 +6793,2 +6794,0 +6795,0 +6796,0 +6797,0 +6798,0 +6799,0 +6800,0 +6801,0 +6802,0 +6803,0 +6804,0 +6805,0 +6806,0 +6807,3 +6808,0 +6809,1 +6810,0 +6811,1 +6812,0 +6813,2 +6814,3 +6815,1 +6816,2 +6817,1 +6818,3 +6819,0 +6820,1 +6821,1 +6822,3 +6823,0 +6824,0 +6825,2 +6826,2 +6827,3 +6828,3 +6829,0 +6830,3 +6831,0 +6832,0 +6833,2 +6834,1 +6835,0 +6836,3 +6837,2 +6838,0 +6839,3 +6840,0 +6841,0 +6842,3 +6843,1 +6844,0 +6845,3 +6846,0 +6847,0 +6848,0 +6849,3 +6850,2 +6851,0 +6852,2 +6853,3 +6854,3 +6855,0 +6856,0 +6857,3 +6858,3 +6859,0 +6860,0 +6861,3 +6862,3 +6863,0 +6864,0 +6865,1 +6866,2 +6867,3 +6868,2 +6869,1 +6870,1 +6871,3 +6872,2 +6873,0 +6874,1 +6875,0 +6876,0 +6877,2 +6878,0 +6879,1 +6880,3 +6881,3 +6882,3 +6883,1 +6884,2 +6885,0 +6886,0 +6887,0 +6888,0 +6889,1 +6890,3 +6891,0 +6892,1 +6893,3 +6894,3 +6895,3 +6896,1 +6897,1 +6898,0 +6899,1 +6900,2 +6901,2 +6902,0 +6903,1 +6904,0 +6905,2 +6906,2 +6907,0 +6908,0 +6909,2 +6910,0 +6911,0 +6912,0 +6913,1 +6914,3 +6915,1 +6916,3 +6917,1 +6918,3 +6919,2 +6920,0 +6921,1 +6922,0 +6923,1 +6924,0 +6925,2 +6926,2 +6927,0 +6928,2 +6929,1 +6930,3 +6931,1 +6932,0 +6933,3 +6934,3 +6935,3 +6936,3 +6937,0 +6938,0 +6939,0 +6940,2 +6941,0 +6942,1 +6943,0 +6944,0 +6945,0 +6946,2 +6947,3 +6948,2 +6949,1 +6950,3 +6951,2 +6952,2 +6953,2 +6954,0 +6955,1 +6956,0 +6957,2 +6958,0 +6959,0 +6960,0 +6961,3 +6962,2 +6963,0 +6964,3 +6965,1 +6966,2 +6967,0 +6968,0 +6969,3 +6970,3 +6971,0 +6972,0 +6973,0 +6974,0 +6975,0 +6976,0 +6977,0 +6978,3 +6979,0 +6980,0 +6981,0 +6982,0 +6983,2 +6984,3 +6985,3 +6986,3 +6987,0 +6988,0 +6989,1 +6990,0 +6991,2 +6992,2 +6993,3 +6994,2 +6995,0 +6996,1 +6997,1 +6998,2 +6999,1 +7000,2 +7001,2 +7002,0 +7003,3 +7004,0 +7005,1 +7006,0 +7007,0 +7008,0 +7009,0 +7010,2 +7011,1 +7012,0 +7013,0 +7014,0 +7015,0 +7016,2 +7017,0 +7018,0 +7019,0 +7020,2 +7021,0 +7022,0 +7023,2 +7024,0 +7025,1 +7026,0 +7027,0 +7028,2 +7029,1 +7030,0 +7031,2 +7032,3 +7033,3 +7034,0 +7035,3 +7036,3 +7037,2 +7038,0 +7039,0 +7040,0 +7041,2 +7042,0 +7043,0 +7044,3 +7045,0 +7046,0 +7047,3 +7048,1 +7049,2 +7050,1 +7051,1 +7052,1 +7053,0 +7054,0 +7055,0 +7056,0 +7057,0 +7058,1 +7059,0 +7060,0 +7061,2 +7062,2 +7063,0 +7064,0 +7065,2 +7066,0 +7067,2 +7068,2 +7069,0 +7070,0 +7071,2 +7072,2 +7073,2 +7074,2 +7075,3 +7076,0 +7077,0 +7078,3 +7079,3 +7080,2 +7081,2 +7082,1 +7083,0 +7084,2 +7085,0 +7086,1 +7087,2 +7088,2 +7089,1 +7090,0 +7091,2 +7092,0 +7093,3 +7094,2 +7095,3 +7096,0 +7097,0 +7098,0 +7099,2 +7100,1 +7101,3 +7102,0 +7103,2 +7104,2 +7105,0 +7106,1 +7107,2 +7108,3 +7109,3 +7110,0 +7111,1 +7112,2 +7113,0 +7114,3 +7115,0 +7116,0 +7117,3 +7118,0 +7119,0 +7120,0 +7121,3 +7122,0 +7123,0 +7124,3 +7125,2 +7126,2 +7127,1 +7128,0 +7129,0 +7130,0 +7131,2 +7132,2 +7133,0 +7134,0 +7135,1 +7136,1 +7137,0 +7138,0 +7139,2 +7140,1 +7141,1 +7142,0 +7143,3 +7144,0 +7145,3 +7146,0 +7147,2 +7148,0 +7149,0 +7150,3 +7151,3 +7152,3 +7153,0 +7154,2 +7155,0 +7156,2 +7157,3 +7158,3 +7159,3 +7160,1 +7161,1 +7162,1 +7163,2 +7164,3 +7165,0 +7166,1 +7167,0 +7168,0 +7169,0 +7170,2 +7171,0 +7172,0 +7173,3 +7174,3 +7175,3 +7176,2 +7177,2 +7178,0 +7179,3 +7180,0 +7181,2 +7182,1 +7183,3 +7184,0 +7185,0 +7186,0 +7187,3 +7188,0 +7189,3 +7190,0 +7191,3 +7192,0 +7193,2 +7194,3 +7195,0 +7196,2 +7197,0 +7198,0 +7199,0 +7200,1 +7201,0 +7202,3 +7203,0 +7204,0 +7205,0 +7206,2 +7207,0 +7208,0 +7209,2 +7210,1 +7211,3 +7212,3 +7213,3 +7214,0 +7215,0 +7216,0 +7217,1 +7218,0 +7219,1 +7220,1 +7221,0 +7222,1 +7223,2 +7224,0 +7225,0 +7226,3 +7227,3 +7228,2 +7229,0 +7230,2 +7231,2 +7232,2 +7233,0 +7234,0 +7235,0 +7236,1 +7237,0 +7238,1 +7239,3 +7240,1 +7241,3 +7242,3 +7243,2 +7244,2 +7245,3 +7246,2 +7247,2 +7248,3 +7249,1 +7250,0 +7251,2 +7252,1 +7253,2 +7254,2 +7255,2 +7256,2 +7257,3 +7258,0 +7259,0 +7260,0 +7261,0 +7262,2 +7263,1 +7264,0 +7265,0 +7266,1 +7267,0 +7268,0 +7269,3 +7270,1 +7271,2 +7272,0 +7273,3 +7274,2 +7275,1 +7276,3 +7277,0 +7278,0 +7279,2 +7280,1 +7281,3 +7282,1 +7283,3 +7284,3 +7285,0 +7286,1 +7287,3 +7288,3 +7289,1 +7290,3 +7291,0 +7292,1 +7293,3 +7294,2 +7295,2 +7296,2 +7297,0 +7298,0 +7299,1 +7300,2 +7301,1 +7302,2 +7303,0 +7304,1 +7305,0 +7306,3 +7307,0 +7308,1 +7309,0 +7310,0 +7311,2 +7312,3 +7313,1 +7314,3 +7315,0 +7316,1 +7317,1 +7318,1 +7319,0 +7320,0 +7321,2 +7322,0 +7323,3 +7324,0 +7325,0 +7326,0 +7327,2 +7328,3 +7329,0 +7330,0 +7331,0 +7332,3 +7333,3 +7334,0 +7335,1 +7336,0 +7337,3 +7338,3 +7339,2 +7340,2 +7341,1 +7342,0 +7343,1 +7344,3 +7345,3 +7346,0 +7347,2 +7348,3 +7349,1 +7350,0 +7351,3 +7352,2 +7353,0 +7354,2 +7355,0 +7356,1 +7357,1 +7358,0 +7359,3 +7360,0 +7361,0 +7362,1 +7363,3 +7364,0 +7365,1 +7366,2 +7367,0 +7368,0 +7369,2 +7370,3 +7371,1 +7372,0 +7373,0 +7374,0 +7375,1 +7376,2 +7377,0 +7378,2 +7379,0 +7380,0 +7381,2 +7382,0 +7383,1 +7384,2 +7385,2 +7386,2 +7387,0 +7388,0 +7389,2 +7390,0 +7391,0 +7392,1 +7393,0 +7394,3 +7395,2 +7396,1 +7397,2 +7398,0 +7399,3 +7400,2 +7401,2 +7402,0 +7403,1 +7404,0 +7405,0 +7406,1 +7407,0 +7408,0 +7409,2 +7410,2 +7411,3 +7412,0 +7413,0 +7414,0 +7415,3 +7416,3 +7417,0 +7418,2 +7419,1 +7420,3 +7421,0 +7422,3 +7423,3 +7424,1 +7425,3 +7426,0 +7427,0 +7428,3 +7429,0 +7430,3 +7431,2 +7432,3 +7433,2 +7434,0 +7435,1 +7436,0 +7437,0 +7438,0 +7439,2 +7440,0 +7441,2 +7442,3 +7443,3 +7444,1 +7445,0 +7446,0 +7447,0 +7448,0 +7449,0 +7450,1 +7451,2 +7452,3 +7453,0 +7454,0 +7455,0 +7456,3 +7457,0 +7458,2 +7459,0 +7460,2 +7461,0 +7462,0 +7463,0 +7464,3 +7465,3 +7466,2 +7467,0 +7468,0 +7469,3 +7470,3 +7471,3 +7472,2 +7473,1 +7474,0 +7475,0 +7476,1 +7477,2 +7478,0 +7479,0 +7480,0 +7481,0 +7482,3 +7483,0 +7484,2 +7485,3 +7486,2 +7487,2 +7488,0 +7489,0 +7490,0 +7491,0 +7492,2 +7493,0 +7494,3 +7495,0 +7496,0 +7497,3 +7498,0 +7499,3 +7500,0 +7501,0 +7502,0 +7503,3 +7504,1 +7505,0 +7506,0 +7507,0 +7508,0 +7509,2 +7510,3 +7511,0 +7512,3 +7513,3 +7514,1 +7515,2 +7516,1 +7517,2 +7518,0 +7519,3 +7520,1 +7521,3 +7522,0 +7523,0 +7524,0 +7525,1 +7526,0 +7527,3 +7528,0 +7529,0 +7530,2 +7531,2 +7532,0 +7533,2 +7534,0 +7535,2 +7536,3 +7537,2 +7538,0 +7539,0 +7540,0 +7541,2 +7542,0 +7543,0 +7544,0 +7545,2 +7546,3 +7547,0 +7548,3 +7549,0 +7550,2 +7551,0 +7552,0 +7553,2 +7554,3 +7555,3 +7556,3 +7557,0 +7558,3 +7559,1 +7560,0 +7561,2 +7562,0 +7563,3 +7564,3 +7565,3 +7566,2 +7567,0 +7568,0 +7569,3 +7570,0 +7571,3 +7572,0 +7573,2 +7574,2 +7575,1 +7576,3 +7577,0 +7578,1 +7579,0 +7580,0 +7581,0 +7582,2 +7583,1 +7584,0 +7585,0 +7586,0 +7587,1 +7588,0 +7589,0 +7590,0 +7591,3 +7592,2 +7593,0 +7594,3 +7595,0 +7596,2 +7597,1 +7598,1 +7599,0 +7600,3 +7601,3 +7602,0 +7603,0 +7604,1 +7605,0 +7606,3 +7607,2 +7608,0 +7609,3 +7610,3 +7611,0 +7612,0 +7613,0 +7614,0 +7615,3 +7616,2 +7617,0 +7618,0 +7619,1 +7620,0 +7621,1 +7622,0 +7623,3 +7624,0 +7625,1 +7626,3 +7627,0 +7628,3 +7629,0 +7630,0 +7631,0 +7632,1 +7633,3 +7634,3 +7635,1 +7636,0 +7637,1 +7638,2 +7639,3 +7640,2 +7641,2 +7642,0 +7643,2 +7644,0 +7645,2 +7646,3 +7647,1 +7648,2 +7649,0 +7650,1 +7651,3 +7652,2 +7653,1 +7654,2 +7655,0 +7656,0 +7657,3 +7658,0 +7659,3 +7660,2 +7661,0 +7662,0 +7663,3 +7664,2 +7665,0 +7666,2 +7667,0 +7668,0 +7669,3 +7670,2 +7671,0 +7672,0 +7673,2 +7674,2 +7675,2 +7676,0 +7677,0 +7678,1 +7679,3 +7680,0 +7681,1 +7682,3 +7683,0 +7684,2 +7685,0 +7686,0 +7687,2 +7688,0 +7689,1 +7690,0 +7691,1 +7692,0 +7693,2 +7694,3 +7695,3 +7696,0 +7697,0 +7698,0 +7699,2 +7700,2 +7701,2 +7702,3 +7703,0 +7704,0 +7705,2 +7706,0 +7707,3 +7708,3 +7709,0 +7710,2 +7711,0 +7712,0 +7713,2 +7714,3 +7715,1 +7716,3 +7717,0 +7718,3 +7719,3 +7720,0 +7721,0 +7722,0 +7723,1 +7724,0 +7725,2 +7726,0 +7727,0 +7728,0 +7729,1 +7730,0 +7731,3 +7732,3 +7733,0 +7734,2 +7735,1 +7736,2 +7737,3 +7738,1 +7739,0 +7740,3 +7741,3 +7742,0 +7743,0 +7744,3 +7745,0 +7746,0 +7747,3 +7748,1 +7749,3 +7750,0 +7751,3 +7752,0 +7753,2 +7754,0 +7755,0 +7756,1 +7757,1 +7758,1 +7759,1 +7760,3 +7761,0 +7762,2 +7763,0 +7764,0 +7765,0 +7766,3 +7767,2 +7768,3 +7769,3 +7770,0 +7771,3 +7772,0 +7773,0 +7774,3 +7775,2 +7776,1 +7777,3 +7778,1 +7779,3 +7780,3 +7781,0 +7782,0 +7783,1 +7784,2 +7785,1 +7786,2 +7787,3 +7788,1 +7789,0 +7790,3 +7791,0 +7792,2 +7793,3 +7794,0 +7795,3 +7796,2 +7797,2 +7798,3 +7799,0 +7800,3 +7801,3 +7802,3 +7803,3 +7804,0 +7805,3 +7806,1 +7807,0 +7808,3 +7809,0 +7810,3 +7811,3 +7812,2 +7813,1 +7814,0 +7815,2 +7816,0 +7817,1 +7818,2 +7819,3 +7820,0 +7821,0 +7822,0 +7823,3 +7824,0 +7825,3 +7826,0 +7827,3 +7828,3 +7829,1 +7830,0 +7831,0 +7832,0 +7833,1 +7834,0 +7835,3 +7836,3 +7837,0 +7838,0 +7839,1 +7840,1 +7841,0 +7842,2 +7843,0 +7844,0 +7845,0 +7846,3 +7847,0 +7848,0 +7849,0 +7850,2 +7851,0 +7852,0 +7853,1 +7854,0 +7855,3 +7856,1 +7857,0 +7858,0 +7859,3 +7860,0 +7861,0 +7862,0 +7863,0 +7864,3 +7865,0 +7866,1 +7867,0 +7868,3 +7869,3 +7870,0 +7871,0 +7872,0 +7873,2 +7874,3 +7875,3 +7876,2 +7877,0 +7878,2 +7879,0 +7880,2 +7881,1 +7882,0 +7883,0 +7884,2 +7885,0 +7886,0 +7887,2 +7888,2 +7889,1 +7890,0 +7891,3 +7892,1 +7893,2 +7894,3 +7895,0 +7896,0 +7897,1 +7898,2 +7899,3 +7900,0 +7901,0 +7902,1 +7903,1 +7904,0 +7905,1 +7906,2 +7907,0 +7908,0 +7909,3 +7910,1 +7911,2 +7912,2 +7913,2 +7914,0 +7915,3 +7916,3 +7917,0 +7918,0 +7919,0 +7920,1 +7921,0 +7922,3 +7923,0 +7924,1 +7925,0 +7926,1 +7927,2 +7928,1 +7929,0 +7930,0 +7931,1 +7932,3 +7933,2 +7934,1 +7935,0 +7936,1 +7937,0 +7938,0 +7939,0 +7940,3 +7941,0 +7942,0 +7943,1 +7944,2 +7945,0 +7946,2 +7947,0 +7948,0 +7949,2 +7950,0 +7951,0 +7952,0 +7953,1 +7954,0 +7955,3 +7956,3 +7957,3 +7958,3 +7959,2 +7960,3 +7961,0 +7962,1 +7963,3 +7964,0 +7965,0 +7966,0 +7967,0 +7968,1 +7969,0 +7970,2 +7971,2 +7972,2 +7973,3 +7974,0 +7975,3 +7976,0 +7977,2 +7978,1 +7979,1 +7980,0 +7981,0 +7982,2 +7983,3 +7984,3 +7985,0 +7986,0 +7987,2 +7988,2 +7989,0 +7990,3 +7991,2 +7992,0 +7993,0 +7994,0 +7995,1 +7996,1 +7997,2 +7998,1 +7999,0 +8000,0 +8001,0 +8002,1 +8003,3 +8004,0 +8005,3 +8006,2 +8007,2 +8008,1 +8009,1 +8010,2 +8011,0 +8012,0 +8013,3 +8014,3 +8015,0 +8016,3 +8017,3 +8018,0 +8019,0 +8020,3 +8021,0 +8022,0 +8023,3 +8024,0 +8025,0 +8026,2 +8027,1 +8028,3 +8029,0 +8030,2 +8031,2 +8032,2 +8033,2 +8034,0 +8035,0 +8036,3 +8037,2 +8038,0 +8039,3 +8040,3 +8041,0 +8042,3 +8043,0 +8044,2 +8045,3 +8046,0 +8047,0 +8048,0 +8049,2 +8050,0 +8051,3 +8052,2 +8053,0 +8054,0 +8055,1 +8056,2 +8057,3 +8058,0 +8059,1 +8060,1 +8061,1 +8062,0 +8063,0 +8064,3 +8065,2 +8066,0 +8067,0 +8068,1 +8069,0 +8070,3 +8071,0 +8072,1 +8073,1 +8074,2 +8075,2 +8076,0 +8077,0 +8078,0 +8079,0 +8080,0 +8081,0 +8082,1 +8083,3 +8084,3 +8085,0 +8086,3 +8087,0 +8088,0 +8089,0 +8090,0 +8091,0 +8092,0 +8093,0 +8094,0 +8095,3 +8096,2 +8097,2 +8098,0 +8099,3 +8100,3 +8101,2 +8102,2 +8103,0 +8104,0 +8105,3 +8106,1 +8107,0 +8108,3 +8109,2 +8110,1 +8111,0 +8112,0 +8113,3 +8114,2 +8115,1 +8116,1 +8117,0 +8118,2 +8119,0 +8120,0 +8121,0 +8122,2 +8123,2 +8124,0 +8125,3 +8126,0 +8127,1 +8128,0 +8129,0 +8130,1 +8131,0 +8132,3 +8133,1 +8134,1 +8135,1 +8136,2 +8137,2 +8138,1 +8139,1 +8140,0 +8141,2 +8142,2 +8143,0 +8144,0 +8145,1 +8146,1 +8147,2 +8148,3 +8149,3 +8150,3 +8151,3 +8152,1 +8153,0 +8154,2 +8155,2 +8156,0 +8157,0 +8158,0 +8159,2 +8160,0 +8161,2 +8162,2 +8163,2 +8164,3 +8165,3 +8166,3 +8167,3 +8168,0 +8169,3 +8170,0 +8171,1 +8172,1 +8173,0 +8174,3 +8175,1 +8176,0 +8177,3 +8178,0 +8179,3 +8180,1 +8181,0 +8182,1 +8183,3 +8184,3 +8185,2 +8186,2 +8187,2 +8188,3 +8189,2 +8190,3 +8191,0 +8192,0 +8193,0 +8194,1 +8195,1 +8196,1 +8197,0 +8198,2 +8199,3 +8200,3 +8201,0 +8202,3 +8203,3 +8204,1 +8205,0 +8206,2 +8207,0 +8208,3 +8209,0 +8210,0 +8211,1 +8212,3 +8213,0 +8214,0 +8215,0 +8216,3 +8217,2 +8218,1 +8219,3 +8220,0 +8221,3 +8222,2 +8223,0 +8224,0 +8225,0 +8226,1 +8227,0 +8228,0 +8229,0 +8230,3 +8231,0 +8232,3 +8233,0 +8234,2 +8235,3 +8236,3 +8237,0 +8238,2 +8239,0 +8240,0 +8241,0 +8242,0 +8243,0 +8244,0 +8245,0 +8246,3 +8247,0 +8248,2 +8249,3 +8250,0 +8251,0 +8252,1 +8253,2 +8254,0 +8255,3 +8256,0 +8257,0 +8258,0 +8259,3 +8260,3 +8261,3 +8262,1 +8263,0 +8264,0 +8265,3 +8266,3 +8267,0 +8268,2 +8269,3 +8270,2 +8271,0 +8272,0 +8273,3 +8274,0 +8275,3 +8276,3 +8277,0 +8278,2 +8279,0 +8280,2 +8281,0 +8282,0 +8283,3 +8284,2 +8285,0 +8286,0 +8287,2 +8288,2 +8289,0 +8290,0 +8291,0 +8292,0 +8293,1 +8294,0 +8295,0 +8296,1 +8297,1 +8298,0 +8299,1 +8300,3 +8301,0 +8302,0 +8303,1 +8304,0 +8305,0 +8306,2 +8307,2 +8308,3 +8309,0 +8310,3 +8311,3 +8312,0 +8313,0 +8314,0 +8315,3 +8316,0 +8317,2 +8318,3 +8319,0 +8320,0 +8321,0 +8322,0 +8323,0 +8324,2 +8325,2 +8326,0 +8327,3 +8328,2 +8329,0 +8330,2 +8331,3 +8332,3 +8333,1 +8334,0 +8335,0 +8336,3 +8337,2 +8338,0 +8339,3 +8340,0 +8341,1 +8342,2 +8343,0 +8344,1 +8345,0 +8346,0 +8347,0 +8348,3 +8349,2 +8350,3 +8351,3 +8352,0 +8353,2 +8354,3 +8355,0 +8356,2 +8357,3 +8358,0 +8359,3 +8360,0 +8361,3 +8362,3 +8363,2 +8364,3 +8365,0 +8366,2 +8367,2 +8368,2 +8369,1 +8370,3 +8371,0 +8372,3 +8373,1 +8374,3 +8375,3 +8376,0 +8377,2 +8378,2 +8379,0 +8380,2 +8381,3 +8382,0 +8383,0 +8384,3 +8385,0 +8386,2 +8387,3 +8388,1 +8389,2 +8390,2 +8391,1 +8392,3 +8393,0 +8394,0 +8395,2 +8396,3 +8397,0 +8398,0 +8399,2 +8400,0 +8401,1 +8402,3 +8403,3 +8404,3 +8405,0 +8406,2 +8407,0 +8408,0 +8409,0 +8410,0 +8411,3 +8412,2 +8413,1 +8414,3 +8415,2 +8416,0 +8417,0 +8418,3 +8419,0 +8420,3 +8421,2 +8422,2 +8423,2 +8424,3 +8425,0 +8426,0 +8427,0 +8428,0 +8429,2 +8430,3 +8431,1 +8432,2 +8433,0 +8434,0 +8435,3 +8436,1 +8437,0 +8438,0 +8439,3 +8440,0 +8441,0 +8442,2 +8443,2 +8444,1 +8445,2 +8446,1 +8447,0 +8448,2 +8449,0 +8450,0 +8451,3 +8452,0 +8453,2 +8454,1 +8455,0 +8456,1 +8457,0 +8458,0 +8459,0 +8460,0 +8461,2 +8462,0 +8463,3 +8464,1 +8465,0 +8466,2 +8467,0 +8468,3 +8469,2 +8470,0 +8471,1 +8472,0 +8473,1 +8474,3 +8475,0 +8476,0 +8477,3 +8478,2 +8479,2 +8480,0 +8481,0 +8482,3 +8483,2 +8484,2 +8485,3 +8486,2 +8487,2 +8488,1 +8489,0 +8490,2 +8491,1 +8492,2 +8493,2 +8494,0 +8495,0 +8496,0 +8497,2 +8498,0 +8499,0 +8500,3 +8501,2 +8502,2 +8503,2 +8504,1 +8505,2 +8506,0 +8507,0 +8508,1 +8509,3 +8510,0 +8511,0 +8512,3 +8513,0 +8514,0 +8515,0 +8516,3 +8517,0 +8518,3 +8519,3 +8520,3 +8521,0 +8522,0 +8523,1 +8524,2 +8525,1 +8526,0 +8527,2 +8528,1 +8529,0 +8530,0 +8531,2 +8532,1 +8533,3 +8534,3 +8535,2 +8536,2 +8537,0 +8538,2 +8539,2 +8540,0 +8541,2 +8542,3 +8543,2 +8544,0 +8545,0 +8546,0 +8547,2 +8548,0 +8549,3 +8550,0 +8551,0 +8552,3 +8553,2 +8554,0 +8555,1 +8556,2 +8557,0 +8558,0 +8559,3 +8560,3 +8561,1 +8562,0 +8563,0 +8564,2 +8565,0 +8566,0 +8567,0 +8568,3 +8569,3 +8570,3 +8571,2 +8572,3 +8573,0 +8574,1 +8575,0 +8576,2 +8577,0 +8578,0 +8579,1 +8580,3 +8581,2 +8582,3 +8583,3 +8584,0 +8585,3 +8586,0 +8587,0 +8588,0 +8589,2 +8590,2 +8591,2 +8592,1 +8593,3 +8594,2 +8595,1 +8596,0 +8597,0 +8598,2 +8599,3 +8600,0 +8601,3 +8602,0 +8603,3 +8604,3 +8605,1 +8606,0 +8607,0 +8608,1 +8609,2 +8610,1 +8611,0 +8612,0 +8613,0 +8614,0 +8615,3 +8616,2 +8617,0 +8618,0 +8619,1 +8620,0 +8621,3 +8622,0 +8623,2 +8624,0 +8625,3 +8626,0 +8627,0 +8628,3 +8629,0 +8630,0 +8631,0 +8632,0 +8633,1 +8634,3 +8635,2 +8636,0 +8637,3 +8638,3 +8639,0 +8640,0 +8641,0 +8642,3 +8643,2 +8644,3 +8645,1 +8646,1 +8647,0 +8648,1 +8649,0 +8650,2 +8651,3 +8652,1 +8653,0 +8654,0 +8655,0 +8656,2 +8657,3 +8658,2 +8659,3 +8660,0 +8661,3 +8662,2 +8663,0 +8664,0 +8665,1 +8666,0 +8667,1 +8668,0 +8669,0 +8670,2 +8671,0 +8672,3 +8673,0 +8674,0 +8675,0 +8676,3 +8677,1 +8678,3 +8679,1 +8680,0 +8681,0 +8682,0 +8683,3 +8684,0 +8685,0 +8686,2 +8687,0 +8688,0 +8689,0 +8690,0 +8691,0 +8692,2 +8693,0 +8694,0 +8695,1 +8696,0 +8697,2 +8698,0 +8699,2 +8700,0 +8701,0 +8702,2 +8703,1 +8704,2 +8705,0 +8706,0 +8707,0 +8708,0 +8709,0 +8710,0 +8711,0 +8712,2 +8713,0 +8714,1 +8715,0 +8716,0 +8717,3 +8718,0 +8719,0 +8720,3 +8721,2 +8722,2 +8723,0 +8724,3 +8725,3 +8726,0 +8727,0 +8728,0 +8729,2 +8730,1 +8731,0 +8732,0 +8733,0 +8734,0 +8735,0 +8736,0 +8737,0 +8738,0 +8739,3 +8740,0 +8741,0 +8742,0 +8743,0 +8744,3 +8745,0 +8746,1 +8747,0 +8748,1 +8749,0 +8750,2 +8751,2 +8752,0 +8753,0 +8754,0 +8755,0 +8756,3 +8757,3 +8758,3 +8759,1 +8760,0 +8761,0 +8762,2 +8763,1 +8764,3 +8765,0 +8766,0 +8767,2 +8768,3 +8769,3 +8770,0 +8771,0 +8772,3 +8773,3 +8774,3 +8775,0 +8776,3 +8777,2 +8778,1 +8779,3 +8780,2 +8781,0 +8782,0 +8783,0 +8784,2 +8785,0 +8786,0 +8787,1 +8788,3 +8789,0 +8790,3 +8791,3 +8792,2 +8793,0 +8794,0 +8795,3 +8796,0 +8797,0 +8798,0 +8799,2 +8800,2 +8801,3 +8802,3 +8803,0 +8804,0 +8805,3 +8806,0 +8807,3 +8808,0 +8809,0 +8810,1 +8811,0 +8812,3 +8813,2 +8814,2 +8815,0 +8816,0 +8817,0 +8818,3 +8819,2 +8820,2 +8821,0 +8822,0 +8823,0 +8824,0 +8825,3 +8826,3 +8827,2 +8828,3 +8829,2 +8830,0 +8831,2 +8832,2 +8833,0 +8834,1 +8835,0 +8836,2 +8837,0 +8838,2 +8839,0 +8840,0 +8841,3 +8842,0 +8843,3 +8844,2 +8845,0 +8846,3 +8847,3 +8848,3 +8849,3 +8850,2 +8851,0 +8852,2 +8853,3 +8854,3 +8855,0 +8856,0 +8857,0 +8858,0 +8859,0 +8860,3 +8861,3 +8862,3 +8863,2 +8864,0 +8865,0 +8866,3 +8867,1 +8868,3 +8869,0 +8870,0 +8871,0 +8872,2 +8873,0 +8874,0 +8875,3 +8876,1 +8877,3 +8878,1 +8879,0 +8880,2 +8881,0 +8882,2 +8883,3 +8884,0 +8885,1 +8886,0 +8887,0 +8888,0 +8889,0 +8890,3 +8891,0 +8892,0 +8893,0 +8894,1 +8895,1 +8896,2 +8897,0 +8898,3 +8899,1 +8900,3 +8901,0 +8902,2 +8903,1 +8904,2 +8905,0 +8906,0 +8907,0 +8908,0 +8909,0 +8910,3 +8911,0 +8912,2 +8913,3 +8914,2 +8915,3 +8916,2 +8917,0 +8918,2 +8919,0 +8920,0 +8921,1 +8922,3 +8923,2 +8924,0 +8925,3 +8926,0 +8927,3 +8928,1 +8929,1 +8930,1 +8931,0 +8932,0 +8933,2 +8934,3 +8935,2 +8936,1 +8937,2 +8938,3 +8939,2 +8940,1 +8941,2 +8942,3 +8943,2 +8944,2 +8945,0 +8946,1 +8947,1 +8948,2 +8949,3 +8950,0 +8951,0 +8952,3 +8953,2 +8954,1 +8955,0 +8956,3 +8957,0 +8958,3 +8959,0 +8960,0 +8961,2 +8962,3 +8963,0 +8964,2 +8965,1 +8966,0 +8967,0 +8968,1 +8969,2 +8970,2 +8971,2 +8972,3 +8973,3 +8974,3 +8975,0 +8976,1 +8977,0 +8978,3 +8979,0 +8980,0 +8981,3 +8982,1 +8983,0 +8984,1 +8985,0 +8986,0 +8987,0 +8988,2 +8989,0 +8990,3 +8991,0 +8992,0 +8993,3 +8994,0 +8995,0 +8996,3 +8997,1 +8998,0 +8999,0 +9000,2 +9001,1 +9002,3 +9003,0 +9004,0 +9005,0 +9006,1 +9007,2 +9008,0 +9009,0 +9010,3 +9011,3 +9012,0 +9013,0 +9014,0 +9015,3 +9016,3 +9017,0 +9018,2 +9019,3 +9020,0 +9021,2 +9022,2 +9023,2 +9024,2 +9025,3 +9026,2 +9027,1 +9028,2 +9029,2 +9030,0 +9031,1 +9032,0 +9033,0 +9034,3 +9035,0 +9036,3 +9037,2 +9038,1 +9039,0 +9040,3 +9041,0 +9042,0 +9043,0 +9044,0 +9045,2 +9046,0 +9047,1 +9048,0 +9049,3 +9050,2 +9051,2 +9052,0 +9053,0 +9054,1 +9055,3 +9056,3 +9057,2 +9058,3 +9059,2 +9060,0 +9061,2 +9062,1 +9063,3 +9064,0 +9065,0 +9066,0 +9067,3 +9068,0 +9069,0 +9070,0 +9071,0 +9072,0 +9073,1 +9074,0 +9075,1 +9076,0 +9077,3 +9078,0 +9079,0 +9080,3 +9081,0 +9082,0 +9083,0 +9084,1 +9085,0 +9086,0 +9087,1 +9088,1 +9089,2 +9090,3 +9091,0 +9092,0 +9093,0 +9094,3 +9095,0 +9096,3 +9097,0 +9098,1 +9099,2 +9100,0 +9101,3 +9102,2 +9103,0 +9104,0 +9105,1 +9106,0 +9107,0 +9108,0 +9109,2 +9110,0 +9111,0 +9112,0 +9113,3 +9114,3 +9115,3 +9116,0 +9117,1 +9118,0 +9119,1 +9120,0 +9121,2 +9122,1 +9123,1 +9124,3 +9125,1 +9126,2 +9127,1 +9128,3 +9129,1 +9130,0 +9131,0 +9132,0 +9133,3 +9134,0 +9135,1 +9136,0 +9137,2 +9138,3 +9139,0 +9140,3 +9141,2 +9142,0 +9143,3 +9144,3 +9145,0 +9146,2 +9147,2 +9148,2 +9149,0 +9150,1 +9151,0 +9152,0 +9153,3 +9154,3 +9155,2 +9156,2 +9157,3 +9158,2 +9159,3 +9160,0 +9161,3 +9162,3 +9163,3 +9164,1 +9165,1 +9166,0 +9167,0 +9168,2 +9169,0 +9170,0 +9171,1 +9172,2 +9173,3 +9174,1 +9175,0 +9176,2 +9177,0 +9178,1 +9179,0 +9180,0 +9181,1 +9182,3 +9183,2 +9184,0 +9185,1 +9186,3 +9187,3 +9188,0 +9189,3 +9190,0 +9191,0 +9192,0 +9193,0 +9194,0 +9195,0 +9196,2 +9197,1 +9198,1 +9199,2 +9200,3 +9201,0 +9202,0 +9203,3 +9204,1 +9205,0 +9206,3 +9207,3 +9208,0 +9209,0 +9210,0 +9211,0 +9212,3 +9213,0 +9214,2 +9215,2 +9216,0 +9217,0 +9218,0 +9219,2 +9220,2 +9221,0 +9222,1 +9223,2 +9224,0 +9225,0 +9226,0 +9227,0 +9228,0 +9229,1 +9230,3 +9231,0 +9232,0 +9233,2 +9234,0 +9235,1 +9236,2 +9237,1 +9238,3 +9239,0 +9240,3 +9241,2 +9242,3 +9243,1 +9244,3 +9245,1 +9246,0 +9247,3 +9248,3 +9249,1 +9250,0 +9251,0 +9252,2 +9253,3 +9254,0 +9255,2 +9256,1 +9257,0 +9258,1 +9259,2 +9260,2 +9261,0 +9262,0 +9263,2 +9264,1 +9265,0 +9266,3 +9267,3 +9268,0 +9269,0 +9270,3 +9271,1 +9272,0 +9273,1 +9274,2 +9275,0 +9276,3 +9277,0 +9278,1 +9279,0 +9280,0 +9281,1 +9282,1 +9283,1 +9284,0 +9285,0 +9286,2 +9287,0 +9288,2 +9289,2 +9290,2 +9291,1 +9292,3 +9293,0 +9294,3 +9295,2 +9296,0 +9297,3 +9298,0 +9299,2 +9300,2 +9301,0 +9302,0 +9303,3 +9304,1 +9305,0 +9306,1 +9307,2 +9308,3 +9309,3 +9310,1 +9311,3 +9312,3 +9313,2 +9314,3 +9315,0 +9316,2 +9317,3 +9318,0 +9319,0 +9320,1 +9321,2 +9322,2 +9323,2 +9324,0 +9325,0 +9326,3 +9327,2 +9328,0 +9329,0 +9330,2 +9331,2 +9332,3 +9333,3 +9334,0 +9335,2 +9336,3 +9337,2 +9338,0 +9339,0 +9340,3 +9341,0 +9342,0 +9343,2 +9344,0 +9345,3 +9346,0 +9347,0 +9348,3 +9349,3 +9350,2 +9351,3 +9352,2 +9353,0 +9354,2 +9355,1 +9356,2 +9357,0 +9358,0 +9359,3 +9360,3 +9361,0 +9362,0 +9363,1 +9364,3 +9365,0 +9366,0 +9367,0 +9368,0 +9369,1 +9370,2 +9371,2 +9372,3 +9373,3 +9374,3 +9375,1 +9376,0 +9377,2 +9378,2 +9379,0 +9380,3 +9381,1 +9382,0 +9383,0 +9384,0 +9385,0 +9386,3 +9387,2 +9388,3 +9389,3 +9390,0 +9391,0 +9392,0 +9393,3 +9394,2 +9395,0 +9396,0 +9397,1 +9398,1 +9399,0 +9400,2 +9401,0 +9402,3 +9403,2 +9404,0 +9405,0 +9406,0 +9407,3 +9408,0 +9409,3 +9410,3 +9411,1 +9412,1 +9413,0 +9414,0 +9415,0 +9416,2 +9417,2 +9418,0 +9419,2 +9420,0 +9421,0 +9422,2 +9423,2 +9424,3 +9425,3 +9426,0 +9427,2 +9428,0 +9429,0 +9430,3 +9431,2 +9432,2 +9433,0 +9434,3 +9435,0 +9436,0 +9437,2 +9438,0 +9439,3 +9440,0 +9441,0 +9442,0 +9443,0 +9444,0 +9445,1 +9446,0 +9447,1 +9448,1 +9449,0 +9450,0 +9451,0 +9452,0 +9453,1 +9454,0 +9455,0 +9456,0 +9457,3 +9458,2 +9459,0 +9460,0 +9461,0 +9462,0 +9463,0 +9464,3 +9465,0 +9466,2 +9467,1 +9468,2 +9469,2 +9470,2 +9471,3 +9472,0 +9473,3 +9474,2 +9475,0 +9476,0 +9477,0 +9478,1 +9479,2 +9480,0 +9481,3 +9482,3 +9483,2 +9484,1 +9485,3 +9486,3 +9487,3 +9488,2 +9489,3 +9490,3 +9491,1 +9492,0 +9493,0 +9494,1 +9495,0 +9496,1 +9497,2 +9498,2 +9499,0 +9500,0 +9501,1 +9502,2 +9503,0 +9504,0 +9505,0 +9506,0 +9507,1 +9508,0 +9509,2 +9510,0 +9511,3 +9512,2 +9513,2 +9514,3 +9515,2 +9516,2 +9517,0 +9518,0 +9519,1 +9520,3 +9521,0 +9522,1 +9523,1 +9524,0 +9525,0 +9526,2 +9527,0 +9528,0 +9529,0 +9530,3 +9531,0 +9532,2 +9533,2 +9534,1 +9535,0 +9536,3 +9537,3 +9538,0 +9539,0 +9540,0 +9541,3 +9542,0 +9543,1 +9544,2 +9545,3 +9546,0 +9547,1 +9548,3 +9549,3 +9550,2 +9551,2 +9552,0 +9553,0 +9554,1 +9555,3 +9556,3 +9557,0 +9558,1 +9559,3 +9560,3 +9561,1 +9562,2 +9563,3 +9564,2 +9565,0 +9566,2 +9567,3 +9568,0 +9569,0 +9570,1 +9571,2 +9572,1 +9573,0 +9574,0 +9575,0 +9576,0 +9577,0 +9578,2 +9579,1 +9580,0 +9581,1 +9582,0 +9583,3 +9584,3 +9585,2 +9586,0 +9587,0 +9588,3 +9589,3 +9590,0 +9591,3 +9592,2 +9593,1 +9594,3 +9595,3 +9596,3 +9597,3 +9598,0 +9599,1 +9600,0 +9601,0 +9602,1 +9603,1 +9604,0 +9605,2 +9606,0 +9607,3 +9608,3 +9609,2 +9610,2 +9611,0 +9612,0 +9613,0 +9614,1 +9615,0 +9616,2 +9617,0 +9618,0 +9619,1 +9620,2 +9621,0 +9622,0 +9623,0 +9624,2 +9625,2 +9626,1 +9627,0 +9628,3 +9629,3 +9630,1 +9631,1 +9632,0 +9633,3 +9634,0 +9635,0 +9636,0 +9637,0 +9638,2 +9639,0 +9640,3 +9641,0 +9642,2 +9643,0 +9644,0 +9645,0 +9646,1 +9647,3 +9648,2 +9649,0 +9650,1 +9651,3 +9652,2 +9653,3 +9654,1 +9655,3 +9656,0 +9657,2 +9658,2 +9659,0 +9660,0 +9661,0 +9662,0 +9663,0 +9664,0 +9665,1 +9666,2 +9667,3 +9668,3 +9669,0 +9670,2 +9671,2 +9672,0 +9673,0 +9674,3 +9675,0 +9676,2 +9677,0 +9678,0 +9679,0 +9680,1 +9681,0 +9682,3 +9683,2 +9684,2 +9685,2 +9686,2 +9687,0 +9688,0 +9689,0 +9690,3 +9691,1 +9692,3 +9693,0 +9694,0 +9695,0 +9696,3 +9697,0 +9698,3 +9699,2 +9700,2 +9701,0 +9702,0 +9703,2 +9704,1 +9705,3 +9706,2 +9707,0 +9708,3 +9709,0 +9710,1 +9711,3 +9712,3 +9713,3 +9714,3 +9715,0 +9716,0 +9717,2 +9718,3 +9719,3 +9720,2 +9721,0 +9722,3 +9723,3 +9724,0 +9725,0 +9726,0 +9727,0 +9728,3 +9729,0 +9730,1 +9731,0 +9732,3 +9733,0 +9734,0 +9735,1 +9736,1 +9737,3 +9738,0 +9739,2 +9740,3 +9741,3 +9742,1 +9743,2 +9744,2 +9745,0 +9746,0 +9747,3 +9748,1 +9749,0 +9750,2 +9751,0 +9752,0 +9753,0 +9754,1 +9755,2 +9756,3 +9757,3 +9758,3 +9759,0 +9760,0 +9761,3 +9762,0 +9763,3 +9764,0 +9765,1 +9766,3 +9767,2 +9768,2 +9769,0 +9770,3 +9771,1 +9772,2 +9773,2 +9774,0 +9775,0 +9776,3 +9777,3 +9778,1 +9779,0 +9780,1 +9781,0 +9782,0 +9783,0 +9784,0 +9785,0 +9786,0 +9787,0 +9788,0 +9789,0 +9790,1 +9791,0 +9792,3 +9793,3 +9794,1 +9795,2 +9796,0 +9797,0 +9798,2 +9799,0 +9800,0 +9801,1 +9802,0 +9803,3 +9804,0 +9805,3 +9806,0 +9807,0 +9808,0 +9809,0 +9810,3 +9811,3 +9812,1 +9813,2 +9814,2 +9815,3 +9816,0 +9817,1 +9818,2 +9819,2 +9820,1 +9821,0 +9822,3 +9823,2 +9824,0 +9825,0 +9826,2 +9827,2 +9828,0 +9829,0 +9830,0 +9831,3 +9832,0 +9833,0 +9834,3 +9835,0 +9836,0 +9837,2 +9838,0 +9839,3 +9840,0 +9841,1 +9842,1 +9843,3 +9844,2 +9845,3 +9846,0 +9847,0 +9848,0 +9849,2 +9850,0 +9851,1 +9852,0 +9853,3 +9854,3 +9855,0 +9856,0 +9857,0 +9858,0 +9859,3 +9860,2 +9861,1 +9862,0 +9863,2 +9864,2 +9865,0 +9866,0 +9867,0 +9868,0 +9869,0 +9870,0 +9871,2 +9872,0 +9873,2 +9874,0 +9875,0 +9876,0 +9877,0 +9878,1 +9879,2 +9880,1 +9881,2 +9882,3 +9883,3 +9884,1 +9885,1 +9886,0 +9887,2 +9888,2 +9889,3 +9890,3 +9891,0 +9892,0 +9893,2 +9894,3 +9895,0 +9896,1 +9897,0 +9898,3 +9899,0 +9900,0 +9901,0 +9902,0 +9903,3 +9904,0 +9905,2 +9906,0 +9907,2 +9908,2 +9909,0 +9910,0 +9911,0 +9912,0 +9913,2 +9914,0 +9915,3 +9916,0 +9917,3 +9918,1 +9919,0 +9920,2 +9921,3 +9922,1 +9923,3 +9924,2 +9925,3 +9926,3 +9927,0 +9928,0 +9929,3 +9930,1 +9931,0 +9932,2 +9933,3 +9934,0 +9935,1 +9936,0 +9937,0 +9938,0 +9939,0 +9940,3 +9941,1 +9942,0 +9943,3 +9944,3 +9945,3 +9946,0 +9947,0 +9948,0 +9949,1 +9950,1 +9951,1 +9952,3 +9953,3 +9954,1 +9955,1 +9956,2 +9957,1 +9958,2 +9959,0 +9960,0 +9961,0 +9962,0 +9963,2 +9964,2 +9965,0 +9966,2 +9967,0 +9968,0 +9969,3 +9970,0 +9971,1 +9972,1 +9973,3 +9974,0 +9975,0 +9976,1 +9977,1 +9978,3 +9979,0 +9980,3 +9981,0 +9982,0 +9983,0 +9984,2 +9985,3 +9986,3 +9987,2 +9988,1 +9989,2 +9990,1 +9991,1 +9992,1 +9993,3 +9994,2 +9995,3 +9996,0 +9997,0 +9998,0 +9999,0 +10000,3 +10001,0 +10002,0 +10003,0 +10004,0 +10005,2 +10006,3 +10007,0 +10008,0 +10009,2 +10010,3 +10011,3 +10012,1 +10013,0 +10014,0 +10015,2 +10016,3 +10017,0 +10018,3 +10019,2 +10020,3 +10021,3 +10022,3 +10023,1 +10024,1 +10025,0 +10026,1 +10027,0 +10028,2 +10029,0 +10030,0 +10031,3 +10032,3 +10033,2 +10034,3 +10035,2 +10036,1 +10037,0 +10038,0 +10039,0 +10040,0 +10041,2 +10042,2 +10043,3 +10044,2 +10045,3 +10046,3 +10047,1 +10048,0 +10049,2 +10050,0 +10051,1 +10052,0 +10053,3 +10054,0 +10055,1 +10056,0 +10057,3 +10058,2 +10059,0 +10060,1 +10061,2 +10062,3 +10063,3 +10064,1 +10065,2 +10066,1 +10067,2 +10068,1 +10069,0 +10070,1 +10071,0 +10072,0 +10073,0 +10074,3 +10075,0 +10076,0 +10077,3 +10078,3 +10079,3 +10080,2 +10081,1 +10082,2 +10083,0 +10084,1 +10085,3 +10086,0 +10087,1 +10088,1 +10089,2 +10090,1 +10091,2 +10092,0 +10093,2 +10094,2 +10095,0 +10096,2 +10097,3 +10098,2 +10099,3 +10100,3 +10101,3 +10102,1 +10103,0 +10104,3 +10105,0 +10106,0 +10107,1 +10108,0 +10109,3 +10110,2 +10111,0 +10112,0 +10113,2 +10114,0 +10115,2 +10116,2 +10117,2 +10118,0 +10119,2 +10120,2 +10121,1 +10122,0 +10123,0 +10124,3 +10125,3 +10126,3 +10127,3 +10128,0 +10129,0 +10130,0 +10131,3 +10132,2 +10133,0 +10134,0 +10135,1 +10136,0 +10137,0 +10138,0 +10139,0 +10140,0 +10141,0 +10142,1 +10143,0 +10144,0 +10145,2 +10146,0 +10147,0 +10148,2 +10149,0 +10150,2 +10151,0 +10152,3 +10153,2 +10154,0 +10155,0 +10156,0 +10157,3 +10158,0 +10159,3 +10160,0 +10161,3 +10162,1 +10163,0 +10164,2 +10165,0 +10166,0 +10167,0 +10168,0 +10169,0 +10170,0 +10171,0 +10172,3 +10173,3 +10174,3 +10175,2 +10176,3 +10177,1 +10178,1 +10179,1 +10180,3 +10181,0 +10182,0 +10183,0 +10184,0 +10185,2 +10186,2 +10187,3 +10188,0 +10189,3 +10190,0 +10191,0 +10192,2 +10193,3 +10194,2 +10195,0 +10196,2 +10197,0 +10198,3 +10199,2 +10200,0 +10201,2 +10202,1 +10203,2 +10204,0 +10205,1 +10206,0 +10207,0 +10208,2 +10209,0 +10210,3 +10211,0 +10212,0 +10213,0 +10214,0 +10215,0 +10216,3 +10217,0 +10218,0 +10219,1 +10220,3 +10221,3 +10222,3 +10223,0 +10224,3 +10225,0 +10226,0 +10227,0 +10228,2 +10229,2 +10230,0 +10231,0 +10232,0 +10233,1 +10234,0 +10235,3 +10236,3 +10237,0 +10238,1 +10239,3 +10240,2 +10241,2 +10242,0 +10243,2 +10244,0 +10245,3 +10246,1 +10247,0 +10248,0 +10249,2 +10250,3 +10251,2 +10252,0 +10253,3 +10254,3 +10255,2 +10256,0 +10257,3 +10258,0 +10259,0 +10260,3 +10261,0 +10262,0 +10263,0 +10264,0 +10265,2 +10266,0 +10267,0 +10268,2 +10269,0 +10270,0 +10271,0 +10272,0 +10273,0 +10274,1 +10275,1 +10276,0 +10277,3 +10278,0 +10279,0 +10280,2 +10281,3 +10282,1 +10283,1 +10284,0 +10285,2 +10286,0 +10287,3 +10288,0 +10289,3 +10290,2 +10291,0 +10292,2 +10293,3 +10294,3 +10295,2 +10296,2 +10297,0 +10298,0 +10299,0 +10300,3 +10301,3 +10302,2 +10303,0 +10304,0 +10305,2 +10306,0 +10307,3 +10308,0 +10309,3 +10310,3 +10311,0 +10312,1 +10313,0 +10314,1 +10315,0 +10316,0 +10317,2 +10318,0 +10319,1 +10320,1 +10321,0 +10322,0 +10323,0 +10324,0 +10325,2 +10326,3 +10327,3 +10328,3 +10329,3 +10330,0 +10331,0 +10332,1 +10333,1 +10334,1 +10335,0 +10336,0 +10337,2 +10338,0 +10339,0 +10340,3 +10341,2 +10342,0 +10343,2 +10344,2 +10345,0 +10346,0 +10347,2 +10348,2 +10349,0 +10350,0 +10351,0 +10352,0 +10353,0 +10354,0 +10355,2 +10356,0 +10357,0 +10358,0 +10359,0 +10360,0 +10361,1 +10362,0 +10363,2 +10364,2 +10365,1 +10366,0 +10367,0 +10368,2 +10369,0 +10370,2 +10371,0 +10372,0 +10373,3 +10374,3 +10375,2 +10376,0 +10377,3 +10378,0 +10379,3 +10380,0 +10381,2 +10382,2 +10383,0 +10384,1 +10385,0 +10386,0 +10387,3 +10388,0 +10389,1 +10390,3 +10391,0 +10392,0 +10393,0 +10394,2 +10395,0 +10396,2 +10397,0 +10398,0 +10399,0 +10400,2 +10401,0 +10402,3 +10403,3 +10404,3 +10405,0 +10406,2 +10407,3 +10408,0 +10409,0 +10410,0 +10411,1 +10412,3 +10413,0 +10414,1 +10415,0 +10416,0 +10417,2 +10418,2 +10419,1 +10420,0 +10421,1 +10422,0 +10423,0 +10424,1 +10425,2 +10426,2 +10427,0 +10428,3 +10429,0 +10430,0 +10431,0 +10432,1 +10433,0 +10434,3 +10435,0 +10436,3 +10437,3 +10438,1 +10439,1 +10440,0 +10441,0 +10442,0 +10443,0 +10444,3 +10445,2 +10446,3 +10447,2 +10448,1 +10449,3 +10450,3 +10451,0 +10452,3 +10453,0 +10454,2 +10455,0 +10456,0 +10457,3 +10458,0 +10459,3 +10460,1 +10461,0 +10462,3 +10463,0 +10464,2 +10465,1 +10466,0 +10467,3 +10468,1 +10469,0 +10470,3 +10471,0 +10472,1 +10473,0 +10474,1 +10475,0 +10476,1 +10477,0 +10478,1 +10479,1 +10480,0 +10481,0 +10482,3 +10483,1 +10484,0 +10485,1 +10486,1 +10487,0 +10488,0 +10489,0 +10490,2 +10491,3 +10492,0 +10493,0 +10494,0 +10495,0 +10496,1 +10497,0 +10498,0 +10499,1 +10500,2 +10501,2 +10502,0 +10503,0 +10504,3 +10505,3 +10506,0 +10507,1 +10508,3 +10509,0 +10510,2 +10511,3 +10512,0 +10513,2 +10514,0 +10515,2 +10516,3 +10517,0 +10518,2 +10519,1 +10520,2 +10521,0 +10522,3 +10523,2 +10524,3 +10525,0 +10526,3 +10527,2 +10528,1 +10529,1 +10530,0 +10531,2 +10532,1 +10533,3 +10534,3 +10535,1 +10536,2 +10537,0 +10538,1 +10539,1 +10540,0 +10541,2 +10542,0 +10543,1 +10544,2 +10545,3 +10546,0 +10547,2 +10548,0 +10549,1 +10550,0 +10551,3 +10552,0 +10553,0 +10554,0 +10555,2 +10556,0 +10557,3 +10558,2 +10559,0 +10560,2 +10561,0 +10562,3 +10563,1 +10564,3 +10565,0 +10566,0 +10567,3 +10568,0 +10569,0 +10570,2 +10571,0 +10572,0 +10573,0 +10574,0 +10575,0 +10576,2 +10577,0 +10578,2 +10579,0 +10580,0 +10581,2 +10582,0 +10583,2 +10584,0 +10585,3 +10586,0 +10587,0 +10588,2 +10589,2 +10590,0 +10591,3 +10592,2 +10593,0 +10594,0 +10595,0 +10596,0 +10597,0 +10598,2 +10599,2 +10600,2 +10601,1 +10602,1 +10603,2 +10604,3 +10605,3 +10606,0 +10607,3 +10608,1 +10609,0 +10610,3 +10611,0 +10612,0 +10613,3 +10614,3 +10615,2 +10616,1 +10617,1 +10618,3 +10619,3 +10620,1 +10621,0 +10622,2 +10623,0 +10624,2 +10625,2 +10626,3 +10627,1 +10628,0 +10629,0 +10630,3 +10631,0 +10632,0 +10633,3 +10634,1 +10635,3 +10636,0 +10637,0 +10638,3 +10639,1 +10640,0 +10641,0 +10642,3 +10643,3 +10644,1 +10645,0 +10646,1 +10647,1 +10648,3 +10649,2 +10650,1 +10651,2 +10652,0 +10653,2 +10654,2 +10655,1 +10656,2 +10657,1 +10658,3 +10659,1 +10660,1 +10661,1 +10662,3 +10663,1 +10664,2 +10665,0 +10666,3 +10667,0 +10668,2 +10669,0 +10670,2 +10671,0 +10672,2 +10673,1 +10674,0 +10675,1 +10676,2 +10677,0 +10678,3 +10679,2 +10680,3 +10681,1 +10682,0 +10683,0 +10684,0 +10685,3 +10686,3 +10687,2 +10688,2 +10689,0 +10690,0 +10691,1 +10692,0 +10693,3 +10694,0 +10695,2 +10696,2 +10697,0 +10698,0 +10699,0 +10700,2 +10701,3 +10702,0 +10703,2 +10704,2 +10705,1 +10706,0 +10707,3 +10708,3 +10709,0 +10710,0 +10711,2 +10712,0 +10713,0 +10714,2 +10715,1 +10716,2 +10717,0 +10718,3 +10719,3 +10720,0 +10721,0 +10722,0 +10723,2 +10724,1 +10725,3 +10726,0 +10727,2 +10728,0 +10729,2 +10730,0 +10731,2 +10732,0 +10733,3 +10734,0 +10735,1 +10736,3 +10737,2 +10738,0 +10739,0 +10740,0 +10741,0 +10742,3 +10743,3 +10744,0 diff --git a/stacker.py b/stacker.py new file mode 100644 index 0000000000000000000000000000000000000000..403968a56ff1c3e441f9f0a1aacf61f738c935c2 --- /dev/null +++ b/stacker.py @@ -0,0 +1,60 @@ +import os +import numpy as np +import pandas as pd +import pickle +import xgboost as xgb + +from sklearn.metrics import accuracy_score, f1_score, confusion_matrix + +from features_extractor import FeatureExtractor + +saved_stacks_path = 'saved_stacks' +saved_stacks_dir = os.listdir(saved_stacks_path) + +y_train_list=[] +y_test_list=[] +y_true_list=[] +for path in saved_stacks_dir: + path = os.path.join(saved_stacks_path, path) + stack_data = pickle.load(open(path, "rb")) + + y_train = stack_data['Y_train_pred'] + y_train_list += [y_train[:, np.newaxis]] + + y_test = stack_data['Y_test_pred'] + y_test_list += [y_test[:, np.newaxis]] + + y_true = stack_data['Y_true_pred'] + y_true_list += [y_true[:, np.newaxis]] + +y_train_med = np.median(np.concatenate(y_train_list, axis=1), axis=1) +y_test_med = np.median(np.concatenate(y_test_list, axis=1), axis=1) +y_true_med = np.median(np.concatenate(y_true_list, axis=1), axis=1) + +pred_df = pd.DataFrame(np.argmax(y_true_med, axis=1), columns=['label']) +pred_df.to_csv("med_sample_submission.csv", index=True, index_label='Id') + +fe = FeatureExtractor(['train.csv', 'train_legacy.csv'], test_size=0.2) + +Y_train = np.ravel(fe.train_df[['label']]) +Y_test = np.ravel(fe.test_df[['label']]) + +stacked_model = xgb.XGBClassifier(n_estimators=300, max_depth=10, + learning_rate=0.02, subsample=0.8, colsample_bytree=0.5, + min_child_weight=0, scale_pos_weight=1, + objective='multi:softprob', verbosity=1) + + +stacked_model.fit(y_train_med, Y_train) + +test_pred = stacked_model.predict(y_test_med) +stacked_score = f1_score(Y_test, test_pred, average=None) +print("Final Test : ", stacked_score) +print(confusion_matrix(Y_test, test_pred)) + +y_true_stacked = stacked_model.predict(y_true_med) +pred_df = pd.DataFrame(np.argmax(y_true_stacked, axis=1), columns=['label']) +pred_df.to_csv("stacked_sample_submission.csv", index=True, index_label='Id') + + + diff --git a/venv/Lib/site-packages/rope-0.14.0.dist-info/DESCRIPTION.rst b/venv/Lib/site-packages/rope-0.14.0.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000000000000000000000000000000000000..d4dc96796ec6d333512937bf912421bc94586bf2 --- /dev/null +++ b/venv/Lib/site-packages/rope-0.14.0.dist-info/DESCRIPTION.rst @@ -0,0 +1,28 @@ + + +.. _GitHub python-rope / rope: https://github.com/python-rope/rope + + +======================================== + rope, a python refactoring library ... +======================================== + + +Overview +======== + +`Rope`_ is a python refactoring library. + +.. _`rope`: https://github.com/python-rope/rope + + +Notes +============ + +* Nick Smith <nicks@fastmail.fm> takes over maintaining rope. Many thanks to + Matej Cepl for his work maintaining rope for the past few years!! +* Partial Python3 support, please file bugs and contribute patches if you + encounter gaps. + + + diff --git a/venv/Lib/site-packages/rope-0.14.0.dist-info/INSTALLER b/venv/Lib/site-packages/rope-0.14.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/venv/Lib/site-packages/rope-0.14.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/rope-0.14.0.dist-info/METADATA b/venv/Lib/site-packages/rope-0.14.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..1393b0da9b9876f50199fabc81c83f3771eb336c --- /dev/null +++ b/venv/Lib/site-packages/rope-0.14.0.dist-info/METADATA @@ -0,0 +1,56 @@ +Metadata-Version: 2.0 +Name: rope +Version: 0.14.0 +Summary: a python refactoring library... +Home-page: https://github.com/python-rope/rope +Author: Ali Gholami Rudi +Author-email: aligrudi@users.sourceforge.net +License: GNU GPL +Description-Content-Type: UNKNOWN +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Operating System :: OS Independent +Classifier: Environment :: X11 Applications +Classifier: Environment :: Win32 (MS Windows) +Classifier: Environment :: MacOS X +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+) +Classifier: Natural Language :: English +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Topic :: Software Development + + + +.. _GitHub python-rope / rope: https://github.com/python-rope/rope + + +======================================== + rope, a python refactoring library ... +======================================== + + +Overview +======== + +`Rope`_ is a python refactoring library. + +.. _`rope`: https://github.com/python-rope/rope + + +Notes +============ + +* Nick Smith <nicks@fastmail.fm> takes over maintaining rope. Many thanks to + Matej Cepl for his work maintaining rope for the past few years!! +* Partial Python3 support, please file bugs and contribute patches if you + encounter gaps. + + + diff --git a/venv/Lib/site-packages/rope-0.14.0.dist-info/RECORD b/venv/Lib/site-packages/rope-0.14.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..924b81c0cd0b7f60120bc7d344c5733b55aaf1e7 --- /dev/null +++ b/venv/Lib/site-packages/rope-0.14.0.dist-info/RECORD @@ -0,0 +1,189 @@ +rope-0.14.0.dist-info/DESCRIPTION.rst,sha256=RzRBs7wozJU1WbkRB1kDYOAXpVPgxTK5tJDkO3imTWQ,566 +rope-0.14.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +rope-0.14.0.dist-info/METADATA,sha256=zm_Ap8SZHzOUF_JRsy6pnDJjAiVX1nokX4xdc09UcFI,1668 +rope-0.14.0.dist-info/RECORD,, +rope-0.14.0.dist-info/WHEEL,sha256=8Lm45v9gcYRm70DrgFGVe4WsUtUMi1_0Tso1hqPGMjA,92 +rope-0.14.0.dist-info/metadata.json,sha256=Q5nkxIXxMTEXdAg2Nftd2S7MQa_cuvMWLmFwdbwhH24,1157 +rope-0.14.0.dist-info/top_level.txt,sha256=9J29TEx1omxK0VahSxM3eA1sZ6iwoJLxY_CXl4eF1K0,5 +rope/__init__.py,sha256=dTX3sZMs5pNrubrJkB01233a8_xcS1G66Lj85y-F-zQ,914 +rope/__pycache__/__init__.cpython-37.pyc,, +rope/base/__init__.py,sha256=a4VjhgbOTCCUwef7_68RbfnHqwVd6L69HKsjRvIlK8s,161 +rope/base/__pycache__/__init__.cpython-37.pyc,, +rope/base/__pycache__/arguments.cpython-37.pyc,, +rope/base/__pycache__/ast.cpython-37.pyc,, +rope/base/__pycache__/astutils.cpython-37.pyc,, +rope/base/__pycache__/builtins.cpython-37.pyc,, +rope/base/__pycache__/change.cpython-37.pyc,, +rope/base/__pycache__/codeanalyze.cpython-37.pyc,, +rope/base/__pycache__/default_config.cpython-37.pyc,, +rope/base/__pycache__/evaluate.cpython-37.pyc,, +rope/base/__pycache__/exceptions.cpython-37.pyc,, +rope/base/__pycache__/fscommands.cpython-37.pyc,, +rope/base/__pycache__/history.cpython-37.pyc,, +rope/base/__pycache__/libutils.cpython-37.pyc,, +rope/base/__pycache__/prefs.cpython-37.pyc,, +rope/base/__pycache__/project.cpython-37.pyc,, +rope/base/__pycache__/pycore.cpython-37.pyc,, +rope/base/__pycache__/pynames.cpython-37.pyc,, +rope/base/__pycache__/pynamesdef.cpython-37.pyc,, +rope/base/__pycache__/pyobjects.cpython-37.pyc,, +rope/base/__pycache__/pyobjectsdef.cpython-37.pyc,, +rope/base/__pycache__/pyscopes.cpython-37.pyc,, +rope/base/__pycache__/resourceobserver.cpython-37.pyc,, +rope/base/__pycache__/resources.cpython-37.pyc,, +rope/base/__pycache__/simplify.cpython-37.pyc,, +rope/base/__pycache__/stdmods.cpython-37.pyc,, +rope/base/__pycache__/taskhandle.cpython-37.pyc,, +rope/base/__pycache__/worder.cpython-37.pyc,, +rope/base/arguments.py,sha256=H1RgB02ZMHSuqjxJ-uYp7dNHVIoD98G29EsSL_uJSKo,3298 +rope/base/ast.py,sha256=cZeaeW3h7vi35lOnY8N-CqXQHMheDrnU6fLvAQuRHoM,2284 +rope/base/astutils.py,sha256=LJ2aj4yvrArILif6eEDGDDridP7eqwM-lxDVP0v_ZQE,1651 +rope/base/builtins.py,sha256=Ow--sZ_JC4sAJqAxn5gQLCOmkuJO7GfzfV9EaASa1rU,26486 +rope/base/change.py,sha256=Kb-25HoJr-eOBcVvmp_O81CzSaHCfWQHK8KFF5na1KI,13575 +rope/base/codeanalyze.py,sha256=MTN3N1WDLk3JokvOyZb75ahz6ibDJeFNItOHMC9bFDU,11011 +rope/base/default_config.py,sha256=6sLNzh3zKZBfx_bb2T7d4wRflUK67OKxdNg4KXMOV5s,4794 +rope/base/evaluate.py,sha256=2jp-wnaJSyVm0W7cEE_Zxd_8n84H7gqqqxfn3dfn7CM,12725 +rope/base/exceptions.py,sha256=GVW7ZhhYGFR2g0lOwpe9lOdTCxoKefP8arFHa1NSEcg,1446 +rope/base/fscommands.py,sha256=HyUpliZr22gfaiDq6KiVxNB4XfkL--evYqFp-7aQcIo,7983 +rope/base/history.py,sha256=_hPV3PSzlv6MUGV4eVBJt-ypS4kzO9kE4ml3IGqJAvI,8654 +rope/base/libutils.py,sha256=GJNwkTj-CLsG4sVX59FzdgXJxODgGlYyTaArn1UXwvg,3952 +rope/base/oi/__init__.py,sha256=_3s_kIc6gY-hUoAguoUDRLcCSDWGDGZhr-pcIm-Y1A0,1684 +rope/base/oi/__pycache__/__init__.cpython-37.pyc,, +rope/base/oi/__pycache__/doa.cpython-37.pyc,, +rope/base/oi/__pycache__/memorydb.cpython-37.pyc,, +rope/base/oi/__pycache__/objectdb.cpython-37.pyc,, +rope/base/oi/__pycache__/objectinfo.cpython-37.pyc,, +rope/base/oi/__pycache__/runmod.cpython-37.pyc,, +rope/base/oi/__pycache__/soa.cpython-37.pyc,, +rope/base/oi/__pycache__/soi.cpython-37.pyc,, +rope/base/oi/__pycache__/transform.cpython-37.pyc,, +rope/base/oi/doa.py,sha256=prknCVfQ8lvVZSslaz_3Mk3mgIkbmeZR0_U6BmGto4k,7310 +rope/base/oi/memorydb.py,sha256=pQ36Cm22wvE4gMl53Di7NBOJusnLNt6WxlnR_6iuyds,3098 +rope/base/oi/objectdb.py,sha256=AmTRp69V-tkPH0UZERXNJKM9BpLFrhZNL1aa9xu_zKY,4753 +rope/base/oi/objectinfo.py,sha256=J0SUOBIrfPvBBPKtHGZmPIetGJuNuCEXp4mhYQIIOoI,8767 +rope/base/oi/runmod.py,sha256=tM5E6a4D7zbdnh1r28D-vkLfWF5GKgi8KIxvU1Bf2iA,8455 +rope/base/oi/soa.py,sha256=0x497SO9LvRBD_3Elkh3_neQJ-DIFg4PFG8NL1raxs8,5636 +rope/base/oi/soi.py,sha256=8InPygj-DGs1db_H69Lu0IyoVysOmEA1Jf0wZoX-i9M,7894 +rope/base/oi/transform.py,sha256=NeMLzSo5fvnQUeSF6wKKRxwLu77kOOnsGc5pQkecMOE,9862 +rope/base/oi/type_hinting/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +rope/base/oi/type_hinting/__pycache__/__init__.cpython-37.pyc,, +rope/base/oi/type_hinting/__pycache__/evaluate.cpython-37.pyc,, +rope/base/oi/type_hinting/__pycache__/factory.cpython-37.pyc,, +rope/base/oi/type_hinting/__pycache__/interfaces.cpython-37.pyc,, +rope/base/oi/type_hinting/__pycache__/utils.cpython-37.pyc,, +rope/base/oi/type_hinting/evaluate.py,sha256=H5seMtt_MCeZ2yNYKHKTOVlzyoduF39N_58g6NFxxQg,9046 +rope/base/oi/type_hinting/factory.py,sha256=bbcrZbKc7-jrpG2CW5gBr9QZU31WAIdZ0_EF6mF0ZBc,2522 +rope/base/oi/type_hinting/interfaces.py,sha256=KMA4ck9o_5o-kmZmXL5tU2xjldBnCvhx24TJoiHWl_8,724 +rope/base/oi/type_hinting/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +rope/base/oi/type_hinting/providers/__pycache__/__init__.cpython-37.pyc,, +rope/base/oi/type_hinting/providers/__pycache__/composite.cpython-37.pyc,, +rope/base/oi/type_hinting/providers/__pycache__/docstrings.cpython-37.pyc,, +rope/base/oi/type_hinting/providers/__pycache__/inheritance.cpython-37.pyc,, +rope/base/oi/type_hinting/providers/__pycache__/interfaces.cpython-37.pyc,, +rope/base/oi/type_hinting/providers/__pycache__/numpydocstrings.cpython-37.pyc,, +rope/base/oi/type_hinting/providers/__pycache__/pep0484_type_comments.cpython-37.pyc,, +rope/base/oi/type_hinting/providers/composite.py,sha256=2W1IZKVBgAHX8dn07EJuxjS35TOvI1UkyzSPodJ_Lb0,1859 +rope/base/oi/type_hinting/providers/docstrings.py,sha256=BA93BWstYuKavH1jji2-Bv3d-p7qpT_SOd6WILBOg74,6194 +rope/base/oi/type_hinting/providers/inheritance.py,sha256=LiFPj2YJJTckKONMdyT6qA_x0aoRYHM98jjvMF7pxY0,2119 +rope/base/oi/type_hinting/providers/interfaces.py,sha256=h4_GsZyiPAHqPL6G7nPfePZDLB9dE0SmQYoIS2Py6_0,1078 +rope/base/oi/type_hinting/providers/numpydocstrings.py,sha256=BCSZbjg5lcd30hNGzye-0b41DCbNgrm9J0XGp4DAG1Q,1380 +rope/base/oi/type_hinting/providers/pep0484_type_comments.py,sha256=FuDJRhKRvxKqm4p8X6xh_SLCrq6ZL-0ZcWF6dxl7B8I,1539 +rope/base/oi/type_hinting/resolvers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +rope/base/oi/type_hinting/resolvers/__pycache__/__init__.cpython-37.pyc,, +rope/base/oi/type_hinting/resolvers/__pycache__/composite.cpython-37.pyc,, +rope/base/oi/type_hinting/resolvers/__pycache__/interfaces.cpython-37.pyc,, +rope/base/oi/type_hinting/resolvers/__pycache__/types.cpython-37.pyc,, +rope/base/oi/type_hinting/resolvers/composite.py,sha256=HcEsazg2fieXSPJpOUweG9PLAB_fzC9JGwMJhumPEis,779 +rope/base/oi/type_hinting/resolvers/interfaces.py,sha256=M8xMYVDxO9Wm-Jd4JF_H4kFU-30AZv39oM4RNBAlupw,414 +rope/base/oi/type_hinting/resolvers/types.py,sha256=MD_bxrIcSJmKnEEfqbeLxZZq-CoxgDBuY2PEBrWNvX8,611 +rope/base/oi/type_hinting/utils.py,sha256=A1WGMpTMuA0AoXtBE2nLWvs_z1QCOEF8Xj6vE5zZO9s,4520 +rope/base/prefs.py,sha256=stjBZ0PCrRvbuotZJw1768rFgURTYcXoGONPSXLpOq4,1094 +rope/base/project.py,sha256=pttN5WKe3FVG1iADgBu5sPAEzQndm1YiOtD2RnUuu7g,16460 +rope/base/pycore.py,sha256=qUc-6k_yZ0bE6z7MxU6kd9le8H7mwKol1t1Bg5oUmkQ,12973 +rope/base/pynames.py,sha256=yvPOPyay3SKe9WGgCgPNld9-mcMUB7hFO_DgQTzlSz4,5931 +rope/base/pynamesdef.py,sha256=ibysKImj7jDUFMFpqm3RN9iHPBiLWpJon72cx1oc2Ig,1719 +rope/base/pyobjects.py,sha256=Zo4cRURuol1dxxA96mcqzEBPck0thh35YJNuywsfkos,8875 +rope/base/pyobjectsdef.py,sha256=lPIsGh7xREP3z2A2Uu0xsTiuBQLibh6F86E9VDP9Knc,20178 +rope/base/pyscopes.py,sha256=zw6zZrQ8qPKKt6O05uK3Ipmz1_O6_2Hv58W8q6j-Wbc,9752 +rope/base/resourceobserver.py,sha256=ZzqcEJp8ZnYVeumeC4y21sNKpXup17E-oyOxOF8eS8g,10280 +rope/base/resources.py,sha256=9U25gYnYfI4-39tU0rFMw7JHw-HXWqBCDStrGA1SGqM,7342 +rope/base/simplify.py,sha256=vnyeDUZ6I5Q-n2Am2E_1KFZTvDmnWQ8IyTIc_339RY8,1692 +rope/base/stdmods.py,sha256=PUVFW-jIRZEc57bZVTTC3qnJ1ZriX1TdOqE0EKiDyes,1868 +rope/base/taskhandle.py,sha256=68_PvCX_mqrJjWq7gTnAUlhTs7KC6cBUO8WcT3w1yDw,2874 +rope/base/utils/__init__.py,sha256=d5BnLRbQcsIZTKB-rSkV09_Mhuy-ElDk__l5xoR1Nbc,2730 +rope/base/utils/__pycache__/__init__.cpython-37.pyc,, +rope/base/utils/__pycache__/datastructures.cpython-37.pyc,, +rope/base/utils/__pycache__/pycompat.cpython-37.pyc,, +rope/base/utils/datastructures.py,sha256=d2OKPP93b6We7_4_0OLkFYSgQUfkDEd9QFbPl25V9BM,1826 +rope/base/utils/pycompat.py,sha256=o8gWnlnQg28NHCa-HIceJMJVIXMbgqUQ31j4hBdGyIw,1127 +rope/base/worder.py,sha256=OHIZ1dQPTfwKzpsW7raf_LzlyH4kW1fj2JkStAZr2oo,21191 +rope/contrib/__init__.py,sha256=2td2_QPauCvImOVmz3a860j2yE-W3stg9BOh1Mb4dlE,169 +rope/contrib/__pycache__/__init__.cpython-37.pyc,, +rope/contrib/__pycache__/autoimport.cpython-37.pyc,, +rope/contrib/__pycache__/changestack.cpython-37.pyc,, +rope/contrib/__pycache__/codeassist.cpython-37.pyc,, +rope/contrib/__pycache__/finderrors.cpython-37.pyc,, +rope/contrib/__pycache__/findit.cpython-37.pyc,, +rope/contrib/__pycache__/fixmodnames.cpython-37.pyc,, +rope/contrib/__pycache__/fixsyntax.cpython-37.pyc,, +rope/contrib/__pycache__/generate.cpython-37.pyc,, +rope/contrib/autoimport.py,sha256=0-BUi1lpq2r7JCQDrkR0zG7ZOqyhHzvs1VV_I0IOK04,8196 +rope/contrib/changestack.py,sha256=sMcj4ugeYf29bLKRwnRXxRjkBTTXnZfkzCsS3Qeu4G8,1392 +rope/contrib/codeassist.py,sha256=zdQhv5RsX32CfPEP7NGcuv6bAWzpibSFL5OC5QGMH0s,26806 +rope/contrib/finderrors.py,sha256=wRaqtDOUIT7vBpm-6qbdzGdPZ8OaE3SvQH76j4EfYnU,2934 +rope/contrib/findit.py,sha256=wcaQf6Pbj6nNi68h4nCVgX4aV_FZGoOjYSFj01DRYOg,4417 +rope/contrib/fixmodnames.py,sha256=oTRRK-g1AyNOVePe7aWppS_usicEm3frq8JcTzVUATc,2241 +rope/contrib/fixsyntax.py,sha256=89677hrUg6vA1CRLp2NiAufc5Mel7R_iSdhXbuBR7ew,6881 +rope/contrib/generate.py,sha256=G4hsobAKb3V-8oBQ1tc2B9oCUFl9pQpZgP5c4xCX7ME,14353 +rope/refactor/__init__.py,sha256=ljN2iK1nG8uzKm8zMWNsFb0hyMTVBu_miIhGz6E4FSY,2097 +rope/refactor/__pycache__/__init__.cpython-37.pyc,, +rope/refactor/__pycache__/change_signature.cpython-37.pyc,, +rope/refactor/__pycache__/encapsulate_field.cpython-37.pyc,, +rope/refactor/__pycache__/extract.cpython-37.pyc,, +rope/refactor/__pycache__/functionutils.cpython-37.pyc,, +rope/refactor/__pycache__/inline.cpython-37.pyc,, +rope/refactor/__pycache__/introduce_factory.cpython-37.pyc,, +rope/refactor/__pycache__/introduce_parameter.cpython-37.pyc,, +rope/refactor/__pycache__/localtofield.cpython-37.pyc,, +rope/refactor/__pycache__/method_object.cpython-37.pyc,, +rope/refactor/__pycache__/move.cpython-37.pyc,, +rope/refactor/__pycache__/multiproject.cpython-37.pyc,, +rope/refactor/__pycache__/occurrences.cpython-37.pyc,, +rope/refactor/__pycache__/patchedast.cpython-37.pyc,, +rope/refactor/__pycache__/rename.cpython-37.pyc,, +rope/refactor/__pycache__/restructure.cpython-37.pyc,, +rope/refactor/__pycache__/similarfinder.cpython-37.pyc,, +rope/refactor/__pycache__/sourceutils.cpython-37.pyc,, +rope/refactor/__pycache__/suites.cpython-37.pyc,, +rope/refactor/__pycache__/topackage.cpython-37.pyc,, +rope/refactor/__pycache__/usefunction.cpython-37.pyc,, +rope/refactor/__pycache__/wildcards.cpython-37.pyc,, +rope/refactor/change_signature.py,sha256=hkBpjINnBVwqc9u4dhGin3nGKsvDku6WNiA__ZhbIBc,13467 +rope/refactor/encapsulate_field.py,sha256=xRGLfJbIV0ZpZFHnok6ES28U5B-B8y0bXjTgb6BGbho,8761 +rope/refactor/extract.py,sha256=A11zvxGtjLskM-r761QDsLHqDbkpYsCU0LkfSXaTPbs,29343 +rope/refactor/functionutils.py,sha256=9ANJ2Ri6x-y3lfsWZu8QRK6Vtu-d_zPciLTucknBwHY,8413 +rope/refactor/importutils/__init__.py,sha256=w7SZAoST_Xad4teWbg79WmcflT8rkEEdAzQYsmAiZ24,13361 +rope/refactor/importutils/__pycache__/__init__.cpython-37.pyc,, +rope/refactor/importutils/__pycache__/actions.cpython-37.pyc,, +rope/refactor/importutils/__pycache__/importinfo.cpython-37.pyc,, +rope/refactor/importutils/__pycache__/module_imports.cpython-37.pyc,, +rope/refactor/importutils/actions.py,sha256=2vIs-_HLwAyZNP8_UudupFgobkl8D2MfWiWVGjREp74,14206 +rope/refactor/importutils/importinfo.py,sha256=HrINeI4YslVwau1qok3TW8_RL4ymTV3mo2yXiiE2kY0,5759 +rope/refactor/importutils/module_imports.py,sha256=9lWkJy92F8uYLJjc5M3zxhR3oMv7aprHgnE7NpFcD_g,19010 +rope/refactor/inline.py,sha256=MOvn8U4pif2RW4o5oISH9Z2Fb2sdX7RaMQA3xRbMM6U,25380 +rope/refactor/introduce_factory.py,sha256=ccjSxVc-1MYfLwfF2Yg4exJziHgWAzKLbd8PGqmbLJE,6190 +rope/refactor/introduce_parameter.py,sha256=yLz6VxjShrZhgCT6REU5mcv0z6_HKZh9FvqEiYLcblI,3908 +rope/refactor/localtofield.py,sha256=7YZMjcn35x8QDkTDGG7mDKKttOF54U79TUGEmniiBIw,2052 +rope/refactor/method_object.py,sha256=YvNRQOTrS6AWTAd4IpXWYOe-hS_aUk3djG_2nndIxgE,3932 +rope/refactor/move.py,sha256=KQ6LIUqZUPd7EoYSZX4nl1ztSCQ2TQXx8C6A3Ss1p8Q,33304 +rope/refactor/multiproject.py,sha256=ngYFgL-J8CLBuT1UONBlZO_nTnAwxtHb5PS2OHH6JzQ,2615 +rope/refactor/occurrences.py,sha256=zbXIU41OIq2q9Ju28jdquGWscqgv6jM94hRgocWG3WU,12841 +rope/refactor/patchedast.py,sha256=42kfkdry0FsFIt_Uhv9AzRZIE_XflwaKtDEFjkrbEL0,28944 +rope/refactor/rename.py,sha256=Du_40DzAo9gVfduoc9pQW-BjT8uHDxtnAX24IpG8d2M,9384 +rope/refactor/restructure.py,sha256=h3QlLi0lN18dPZmyyaLMXzzOlCo00antwATGzHcuALs,11595 +rope/refactor/similarfinder.py,sha256=VERdp7zee1ePyvNPGapwJL062SMD1PnCL3gGON7kF10,12486 +rope/refactor/sourceutils.py,sha256=RKDJ7iXiE95M-vXefG1zpmvn6yhIkIlRlKeCFzPDsr4,2955 +rope/refactor/suites.py,sha256=CF47hvELKPXImS9esJKFl2FVFlEUZshBUGasLTC_ieU,4931 +rope/refactor/topackage.py,sha256=2INH2Gw5c8-b4518wqWaTPOFyo4ZFg_zLzg50ODLEak,1269 +rope/refactor/usefunction.py,sha256=zGhm0wldnsXiHBQ-uYrCR2q904itOvcVkKXndLQRh3s,6280 +rope/refactor/wildcards.py,sha256=lrqS0oeWcY9yoOfPjgghdXRLRMPCDPp0_dIZFy4KTgY,5833 diff --git a/venv/Lib/site-packages/rope-0.14.0.dist-info/WHEEL b/venv/Lib/site-packages/rope-0.14.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..6261a26e7a4c380ad73a4316b8a18d8fa839205d --- /dev/null +++ b/venv/Lib/site-packages/rope-0.14.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.30.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/rope-0.14.0.dist-info/metadata.json b/venv/Lib/site-packages/rope-0.14.0.dist-info/metadata.json new file mode 100644 index 0000000000000000000000000000000000000000..6263a8e5e1798fd2682dc1bdd5828b3cc8f91cb3 --- /dev/null +++ b/venv/Lib/site-packages/rope-0.14.0.dist-info/metadata.json @@ -0,0 +1 @@ +{"classifiers": ["Development Status :: 4 - Beta", "Operating System :: OS Independent", "Environment :: X11 Applications", "Environment :: Win32 (MS Windows)", "Environment :: MacOS X", "Intended Audience :: Developers", "License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)", "Natural Language :: English", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Topic :: Software Development"], "description_content_type": "UNKNOWN", "extensions": {"python.details": {"contacts": [{"email": "aligrudi@users.sourceforge.net", "name": "Ali Gholami Rudi", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/python-rope/rope"}}}, "generator": "bdist_wheel (0.30.0)", "license": "GNU GPL", "metadata_version": "2.0", "name": "rope", "summary": "a python refactoring library...", "version": "0.14.0"} \ No newline at end of file diff --git a/venv/Lib/site-packages/rope-0.14.0.dist-info/top_level.txt b/venv/Lib/site-packages/rope-0.14.0.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..649b5f000e3b85ccdbfb79b5988eb2a721940b30 --- /dev/null +++ b/venv/Lib/site-packages/rope-0.14.0.dist-info/top_level.txt @@ -0,0 +1 @@ +rope diff --git a/venv/Lib/site-packages/rope/__init__.py b/venv/Lib/site-packages/rope/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f85a713438986debf349ffff2f847179d9d296d8 --- /dev/null +++ b/venv/Lib/site-packages/rope/__init__.py @@ -0,0 +1,24 @@ +"""rope, a python refactoring library""" + +INFO = __doc__ +VERSION = '0.14.0' +COPYRIGHT = """\ +Copyright (C) 2019 Matej Cepl +Copyright (C) 2015-2018 Nicholas Smith +Copyright (C) 2014-2015 Matej Cepl +Copyright (C) 2006-2012 Ali Gholami Rudi +Copyright (C) 2009-2012 Anton Gritsay + +This program is free software: you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation, either +version 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this program. If not, see +<https://www.gnu.org/licenses/>.""" diff --git a/venv/Lib/site-packages/rope/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/rope/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..789f178109641dd890a26e97dda1a3f5c4503f29 Binary files /dev/null and b/venv/Lib/site-packages/rope/__pycache__/__init__.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__init__.py b/venv/Lib/site-packages/rope/base/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ff5f8c63ae9daa3003f17b157039e0562837ee48 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/__init__.py @@ -0,0 +1,8 @@ +"""Base rope package + +This package contains rope core modules that are used by other modules +and packages. + +""" + +__all__ = ['project', 'libutils', 'exceptions'] diff --git a/venv/Lib/site-packages/rope/base/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae53efd4d6a0df0d478b5a5482be7dd10b0af421 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/__init__.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/arguments.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/arguments.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7148841f18fcc5be818a93b7f39a48ed24a7adf9 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/arguments.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/ast.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/ast.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2f4591894896ef2145d3005864fec180191a93b3 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/ast.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/astutils.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/astutils.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc25e971ed6ef5934b55ed775329c4fc56f4aff4 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/astutils.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/builtins.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/builtins.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..837fcf91f929cd977bdf41b8f8c595fcc84dcffb Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/builtins.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/change.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/change.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ec09e0cfeec843c0f4526cf0735d0f3fefdcca5 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/change.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/codeanalyze.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/codeanalyze.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..865fd7c5450004e563edbd29e1d715fff385988b Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/codeanalyze.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/default_config.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/default_config.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f0355a2ab9d3b77c3827cd178fa305421edc1238 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/default_config.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/evaluate.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/evaluate.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd41d1422d7e1ffdc56c031dbce42110f49bd153 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/evaluate.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/exceptions.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/exceptions.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6c9b4f1c6102142a101b9eaa8006676ae9dc1811 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/exceptions.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/fscommands.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/fscommands.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca8f6dbeec6052d1f9e1a4257ea51e2e9f49be61 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/fscommands.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/history.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/history.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a87810228bdd731c97d9ef5fd17487238285e39e Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/history.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/libutils.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/libutils.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..47ec606966278f35edb2fca62667ccfb46fa1a0b Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/libutils.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/prefs.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/prefs.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..71907c1431044e2ea1e8ccec76f0e7f31b9e3849 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/prefs.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/project.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/project.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e98c39d7e4dc9656078581e67226be582ba22faf Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/project.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/pycore.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/pycore.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..acca53e98ddfb8e75bfb713da7f2cb95a63d500e Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/pycore.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/pynames.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/pynames.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5d3e891c65207151a7af810aace9cc813ac4533c Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/pynames.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/pynamesdef.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/pynamesdef.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..af8f16959370eaccea4b39b5d0181df754c57c4d Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/pynamesdef.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/pyobjects.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/pyobjects.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cee82f9851872d205d5f257ef05562c9961d38b4 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/pyobjects.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/pyobjectsdef.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/pyobjectsdef.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d68f802f81b57bad229ab335a97b370774b2795e Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/pyobjectsdef.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/pyscopes.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/pyscopes.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ba43a6e987b341575a127697675dfdd96764b2a Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/pyscopes.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/resourceobserver.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/resourceobserver.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c620da3c4e30e05a52349d953c422456ef59957 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/resourceobserver.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/resources.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/resources.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b0ad295532f096671a5660a8db34ff0a59213b4 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/resources.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/simplify.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/simplify.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3a2dd8c4833c2e7ac872673dcf0e8057ac9215f2 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/simplify.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/stdmods.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/stdmods.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..28749a0f788767758e22b7b37981e797f972f5e6 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/stdmods.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/taskhandle.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/taskhandle.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3c48f28ca204c18e4d463e8adfa927546f6e30d8 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/taskhandle.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/__pycache__/worder.cpython-37.pyc b/venv/Lib/site-packages/rope/base/__pycache__/worder.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..af32380d82a16179865c4e2e2d20c11809da59a6 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/__pycache__/worder.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/arguments.py b/venv/Lib/site-packages/rope/base/arguments.py new file mode 100644 index 0000000000000000000000000000000000000000..7ba43640663f56918921fb668285b897a1deaefa --- /dev/null +++ b/venv/Lib/site-packages/rope/base/arguments.py @@ -0,0 +1,111 @@ +import rope.base.evaluate +from rope.base import ast + + +class Arguments(object): + """A class for evaluating parameters passed to a function + + You can use the `create_arguments` factory. It handles implicit + first arguments. + + """ + + def __init__(self, args, scope): + self.args = args + self.scope = scope + self.instance = None + + def get_arguments(self, parameters): + result = [] + for pyname in self.get_pynames(parameters): + if pyname is None: + result.append(None) + else: + result.append(pyname.get_object()) + return result + + def get_pynames(self, parameters): + result = [None] * max(len(parameters), len(self.args)) + for index, arg in enumerate(self.args): + if isinstance(arg, ast.keyword) and arg.arg in parameters: + result[parameters.index(arg.arg)] = self._evaluate(arg.value) + else: + result[index] = self._evaluate(arg) + return result + + def get_instance_pyname(self): + if self.args: + return self._evaluate(self.args[0]) + + def _evaluate(self, ast_node): + return rope.base.evaluate.eval_node(self.scope, ast_node) + + +def create_arguments(primary, pyfunction, call_node, scope): + """A factory for creating `Arguments`""" + args = list(call_node.args) + args.extend(call_node.keywords) + called = call_node.func + # XXX: Handle constructors + if _is_method_call(primary, pyfunction) and \ + isinstance(called, ast.Attribute): + args.insert(0, called.value) + return Arguments(args, scope) + + +class ObjectArguments(object): + + def __init__(self, pynames): + self.pynames = pynames + + def get_arguments(self, parameters): + result = [] + for pyname in self.pynames: + if pyname is None: + result.append(None) + else: + result.append(pyname.get_object()) + return result + + def get_pynames(self, parameters): + return self.pynames + + def get_instance_pyname(self): + return self.pynames[0] + + +class MixedArguments(object): + + def __init__(self, pyname, arguments, scope): + """`argumens` is an instance of `Arguments`""" + self.pyname = pyname + self.args = arguments + + def get_pynames(self, parameters): + return [self.pyname] + self.args.get_pynames(parameters[1:]) + + def get_arguments(self, parameters): + result = [] + for pyname in self.get_pynames(parameters): + if pyname is None: + result.append(None) + else: + result.append(pyname.get_object()) + return result + + def get_instance_pyname(self): + return self.pyname + + +def _is_method_call(primary, pyfunction): + if primary is None: + return False + pyobject = primary.get_object() + if isinstance(pyobject.get_type(), rope.base.pyobjects.PyClass) and \ + isinstance(pyfunction, rope.base.pyobjects.PyFunction) and \ + isinstance(pyfunction.parent, rope.base.pyobjects.PyClass): + return True + if isinstance(pyobject.get_type(), rope.base.pyobjects.AbstractClass) and \ + isinstance(pyfunction, rope.base.builtins.BuiltinFunction): + return True + return False diff --git a/venv/Lib/site-packages/rope/base/ast.py b/venv/Lib/site-packages/rope/base/ast.py new file mode 100644 index 0000000000000000000000000000000000000000..d43c83c55bed265f936352df2393c52d059d81d4 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/ast.py @@ -0,0 +1,76 @@ +import _ast +from _ast import * + +from rope.base import fscommands + +try: + unicode +except NameError: + unicode = str + + +def parse(source, filename='<string>'): + # NOTE: the raw string should be given to `compile` function + if isinstance(source, unicode): + source = fscommands.unicode_to_file_data(source) + if b'\r' in source: + source = source.replace(b'\r\n', b'\n').replace(b'\r', b'\n') + if not source.endswith(b'\n'): + source += b'\n' + try: + return compile(source, filename, 'exec', _ast.PyCF_ONLY_AST) + except (TypeError, ValueError) as e: + error = SyntaxError() + error.lineno = 1 + error.filename = filename + error.msg = str(e) + raise error + + +def walk(node, walker): + """Walk the syntax tree""" + method_name = '_' + node.__class__.__name__ + method = getattr(walker, method_name, None) + if method is not None: + if isinstance(node, _ast.ImportFrom) and node.module is None: + # In python < 2.7 ``node.module == ''`` for relative imports + # but for python 2.7 it is None. Generalizing it to ''. + node.module = '' + return method(node) + for child in get_child_nodes(node): + walk(child, walker) + + +def get_child_nodes(node): + if isinstance(node, _ast.Module): + return node.body + result = [] + if node._fields is not None: + for name in node._fields: + child = getattr(node, name) + if isinstance(child, list): + for entry in child: + if isinstance(entry, _ast.AST): + result.append(entry) + if isinstance(child, _ast.AST): + result.append(child) + return result + + +def call_for_nodes(node, callback, recursive=False): + """If callback returns `True` the child nodes are skipped""" + result = callback(node) + if recursive and not result: + for child in get_child_nodes(node): + call_for_nodes(child, callback, recursive) + + +def get_children(node): + result = [] + if node._fields is not None: + for name in node._fields: + if name in ['lineno', 'col_offset']: + continue + child = getattr(node, name) + result.append(child) + return result diff --git a/venv/Lib/site-packages/rope/base/astutils.py b/venv/Lib/site-packages/rope/base/astutils.py new file mode 100644 index 0000000000000000000000000000000000000000..6c0b3d78e2e5d198be10a6bb339c8e5de2e36c97 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/astutils.py @@ -0,0 +1,64 @@ +from rope.base import ast + + +def get_name_levels(node): + """Return a list of ``(name, level)`` tuples for assigned names + + The `level` is `None` for simple assignments and is a list of + numbers for tuple assignments for example in:: + + a, (b, c) = x + + The levels for for `a` is ``[0]``, for `b` is ``[1, 0]`` and for + `c` is ``[1, 1]``. + + """ + visitor = _NodeNameCollector() + ast.walk(node, visitor) + return visitor.names + + +class _NodeNameCollector(object): + + def __init__(self, levels=None): + self.names = [] + self.levels = levels + self.index = 0 + + def _add_node(self, node): + new_levels = [] + if self.levels is not None: + new_levels = list(self.levels) + new_levels.append(self.index) + self.index += 1 + self._added(node, new_levels) + + def _added(self, node, levels): + if hasattr(node, 'id'): + self.names.append((node.id, levels)) + + def _Name(self, node): + self._add_node(node) + + def _ExceptHandler(self, node): + self.names.append((node.name, [])) + + def _Tuple(self, node): + new_levels = [] + if self.levels is not None: + new_levels = list(self.levels) + new_levels.append(self.index) + self.index += 1 + visitor = _NodeNameCollector(new_levels) + for child in ast.get_child_nodes(node): + ast.walk(child, visitor) + self.names.extend(visitor.names) + + def _Subscript(self, node): + self._add_node(node) + + def _Attribute(self, node): + self._add_node(node) + + def _Slice(self, node): + self._add_node(node) diff --git a/venv/Lib/site-packages/rope/base/builtins.py b/venv/Lib/site-packages/rope/base/builtins.py new file mode 100644 index 0000000000000000000000000000000000000000..bd3d9381d5af72de87c563cd389834930902fcdc --- /dev/null +++ b/venv/Lib/site-packages/rope/base/builtins.py @@ -0,0 +1,826 @@ +"""This module trys to support builtin types and functions.""" +import inspect +import io + +try: + raw_input +except NameError: + raw_input = input + +import rope.base.evaluate +from rope.base.utils import pycompat +from rope.base import pynames, pyobjects, arguments, utils + + +class BuiltinModule(pyobjects.AbstractModule): + + def __init__(self, name, pycore=None, initial={}): + super(BuiltinModule, self).__init__() + self.name = name + self.pycore = pycore + self.initial = initial + + parent = None + + def get_attributes(self): + return self.attributes + + def get_doc(self): + if self.module: + return self.module.__doc__ + + def get_name(self): + return self.name.split('.')[-1] + + @property + @utils.saveit + def attributes(self): + result = _object_attributes(self.module, self) + result.update(self.initial) + if self.pycore is not None: + submodules = self.pycore._builtin_submodules(self.name) + for name, module in submodules.items(): + result[name] = rope.base.builtins.BuiltinName(module) + return result + + @property + @utils.saveit + def module(self): + try: + result = __import__(self.name) + for token in self.name.split('.')[1:]: + result = getattr(result, token, None) + return result + except ImportError: + return + + +class _BuiltinElement(object): + + def __init__(self, builtin, parent=None): + self.builtin = builtin + self._parent = parent + + def get_doc(self): + if self.builtin: + return getattr(self.builtin, '__doc__', None) + + def get_name(self): + if self.builtin: + return getattr(self.builtin, '__name__', None) + + @property + def parent(self): + if self._parent is None: + return builtins + return self._parent + + +class BuiltinClass(_BuiltinElement, pyobjects.AbstractClass): + + def __init__(self, builtin, attributes, parent=None): + _BuiltinElement.__init__(self, builtin, parent) + pyobjects.AbstractClass.__init__(self) + self.initial = attributes + + @utils.saveit + def get_attributes(self): + result = _object_attributes(self.builtin, self) + result.update(self.initial) + return result + + def get_module(self): + return builtins + + +class BuiltinFunction(_BuiltinElement, pyobjects.AbstractFunction): + + def __init__(self, returned=None, function=None, builtin=None, + argnames=[], parent=None): + _BuiltinElement.__init__(self, builtin, parent) + pyobjects.AbstractFunction.__init__(self) + self.argnames = argnames + self.returned = returned + self.function = function + + def get_returned_object(self, args): + if self.function is not None: + return self.function(_CallContext(self.argnames, args)) + else: + return self.returned + + def get_param_names(self, special_args=True): + return self.argnames + + +class BuiltinUnknown(_BuiltinElement, pyobjects.PyObject): + + def __init__(self, builtin): + super(BuiltinUnknown, self).__init__(pyobjects.get_unknown()) + self.builtin = builtin + self.type = pyobjects.get_unknown() + + def get_name(self): + return getattr(type(self.builtin), '__name__', None) + + @utils.saveit + def get_attributes(self): + return _object_attributes(self.builtin, self) + + +def _object_attributes(obj, parent): + attributes = {} + for name in dir(obj): + if name == 'None': + continue + try: + child = getattr(obj, name) + except AttributeError: + # descriptors are allowed to raise AttributeError + # even if they are in dir() + continue + pyobject = None + if inspect.isclass(child): + pyobject = BuiltinClass(child, {}, parent=parent) + elif inspect.isroutine(child): + pyobject = BuiltinFunction(builtin=child, parent=parent) + else: + pyobject = BuiltinUnknown(builtin=child) + attributes[name] = BuiltinName(pyobject) + return attributes + + +def _create_builtin_type_getter(cls): + def _get_builtin(*args): + if not hasattr(cls, '_generated'): + cls._generated = {} + if args not in cls._generated: + cls._generated[args] = cls(*args) + return cls._generated[args] + return _get_builtin + + +def _create_builtin_getter(cls): + type_getter = _create_builtin_type_getter(cls) + + def _get_builtin(*args): + return pyobjects.PyObject(type_getter(*args)) + return _get_builtin + + +class _CallContext(object): + + def __init__(self, argnames, args): + self.argnames = argnames + self.args = args + + def _get_scope_and_pyname(self, pyname): + if pyname is not None and isinstance(pyname, pynames.AssignedName): + pymodule, lineno = pyname.get_definition_location() + if pymodule is None: + return None, None + if lineno is None: + lineno = 1 + scope = pymodule.get_scope().get_inner_scope_for_line(lineno) + name = None + while name is None and scope is not None: + for current in scope.get_names(): + if scope[current] is pyname: + name = current + break + else: + scope = scope.parent + return scope, name + return None, None + + def get_argument(self, name): + if self.args: + args = self.args.get_arguments(self.argnames) + return args[self.argnames.index(name)] + + def get_pyname(self, name): + if self.args: + args = self.args.get_pynames(self.argnames) + if name in self.argnames: + return args[self.argnames.index(name)] + + def get_arguments(self, argnames): + if self.args: + return self.args.get_arguments(argnames) + + def get_pynames(self, argnames): + if self.args: + return self.args.get_pynames(argnames) + + def get_per_name(self): + if self.args is None: + return None + pyname = self.args.get_instance_pyname() + scope, name = self._get_scope_and_pyname(pyname) + if name is not None: + pymodule = pyname.get_definition_location()[0] + return pymodule.pycore.object_info.get_per_name(scope, name) + return None + + def save_per_name(self, value): + if self.args is None: + return None + pyname = self.args.get_instance_pyname() + scope, name = self._get_scope_and_pyname(pyname) + if name is not None: + pymodule = pyname.get_definition_location()[0] + pymodule.pycore.object_info.save_per_name(scope, name, value) + + +class _AttributeCollector(object): + + def __init__(self, type): + self.attributes = {} + self.type = type + + def __call__(self, name, returned=None, function=None, + argnames=['self'], check_existence=True, + parent=None): + try: + builtin = getattr(self.type, name) + except AttributeError: + if check_existence: + raise + builtin = None + self.attributes[name] = BuiltinName( + BuiltinFunction(returned=returned, function=function, + argnames=argnames, builtin=builtin, + parent=parent)) + + def __setitem__(self, name, value): + self.attributes[name] = value + + +class List(BuiltinClass): + + def __init__(self, holding=None): + self.holding = holding + collector = _AttributeCollector(list) + + collector('__iter__', function=self._iterator_get, parent=self) + collector('__new__', function=self._new_list, parent=self) + + # Adding methods + collector('append', function=self._list_add, + argnames=['self', 'value'], parent=self) + collector('__setitem__', function=self._list_add, + argnames=['self', 'index', 'value'], parent=self) + collector('insert', function=self._list_add, + argnames=['self', 'index', 'value'], parent=self) + collector('extend', function=self._self_set, + argnames=['self', 'iterable'], parent=self) + + # Getting methods + collector('__getitem__', function=self._list_get, parent=self) + collector('pop', function=self._list_get, parent=self) + try: + collector('__getslice__', function=self._list_get) + except AttributeError: + pass + + super(List, self).__init__(list, collector.attributes) + + def _new_list(self, args): + return _create_builtin(args, get_list) + + def _list_add(self, context): + if self.holding is not None: + return + holding = context.get_argument('value') + if holding is not None and holding != pyobjects.get_unknown(): + context.save_per_name(holding) + + def _self_set(self, context): + if self.holding is not None: + return + iterable = context.get_pyname('iterable') + holding = _infer_sequence_for_pyname(iterable) + if holding is not None and holding != pyobjects.get_unknown(): + context.save_per_name(holding) + + def _list_get(self, context): + if self.holding is not None: + args = context.get_arguments(['self', 'key']) + if (len(args) > 1 and args[1] is not None and + args[1].get_type() == builtins['slice'].get_object()): + return get_list(self.holding) + return self.holding + return context.get_per_name() + + def _iterator_get(self, context): + return get_iterator(self._list_get(context)) + + def _self_get(self, context): + return get_list(self._list_get(context)) + + +get_list = _create_builtin_getter(List) +get_list_type = _create_builtin_type_getter(List) + + +class Dict(BuiltinClass): + + def __init__(self, keys=None, values=None): + self.keys = keys + self.values = values + collector = _AttributeCollector(dict) + collector('__new__', function=self._new_dict, parent=self) + collector('__setitem__', function=self._dict_add, parent=self) + collector('popitem', function=self._item_get, parent=self) + collector('pop', function=self._value_get, parent=self) + collector('get', function=self._key_get, parent=self) + collector('keys', function=self._key_list, parent=self) + collector('values', function=self._value_list, parent=self) + collector('items', function=self._item_list, parent=self) + collector('copy', function=self._self_get, parent=self) + collector('__getitem__', function=self._value_get, parent=self) + collector('__iter__', function=self._key_iter, parent=self) + collector('update', function=self._self_set, parent=self) + super(Dict, self).__init__(dict, collector.attributes) + + def _new_dict(self, args): + def do_create(holding=None): + if holding is None: + return get_dict() + type = holding.get_type() + if isinstance(type, Tuple) and \ + len(type.get_holding_objects()) == 2: + return get_dict(*type.get_holding_objects()) + return _create_builtin(args, do_create) + + def _dict_add(self, context): + if self.keys is not None: + return + key, value = context.get_arguments(['self', 'key', 'value'])[1:] + if key is not None and key != pyobjects.get_unknown(): + context.save_per_name(get_tuple(key, value)) + + def _item_get(self, context): + if self.keys is not None: + return get_tuple(self.keys, self.values) + item = context.get_per_name() + if item is None or not isinstance(item.get_type(), Tuple): + return get_tuple(self.keys, self.values) + return item + + def _value_get(self, context): + item = self._item_get(context).get_type() + return item.get_holding_objects()[1] + + def _key_get(self, context): + item = self._item_get(context).get_type() + return item.get_holding_objects()[0] + + def _value_list(self, context): + return get_list(self._value_get(context)) + + def _key_list(self, context): + return get_list(self._key_get(context)) + + def _item_list(self, context): + return get_list(self._item_get(context)) + + def _value_iter(self, context): + return get_iterator(self._value_get(context)) + + def _key_iter(self, context): + return get_iterator(self._key_get(context)) + + def _item_iter(self, context): + return get_iterator(self._item_get(context)) + + def _self_get(self, context): + item = self._item_get(context).get_type() + key, value = item.get_holding_objects()[:2] + return get_dict(key, value) + + def _self_set(self, context): + if self.keys is not None: + return + new_dict = context.get_pynames(['self', 'd'])[1] + if new_dict and isinstance(new_dict.get_object().get_type(), Dict): + args = arguments.ObjectArguments([new_dict]) + items = new_dict.get_object()['popitem'].\ + get_object().get_returned_object(args) + context.save_per_name(items) + else: + holding = _infer_sequence_for_pyname(new_dict) + if holding is not None and isinstance(holding.get_type(), Tuple): + context.save_per_name(holding) + + +get_dict = _create_builtin_getter(Dict) +get_dict_type = _create_builtin_type_getter(Dict) + + +class Tuple(BuiltinClass): + + def __init__(self, *objects): + self.objects = objects + first = None + if objects: + first = objects[0] + attributes = { + '__getitem__': BuiltinName(BuiltinFunction(first)), # TODO: add slice support + '__getslice__': + BuiltinName(BuiltinFunction(pyobjects.PyObject(self))), + '__new__': BuiltinName(BuiltinFunction(function=self._new_tuple)), + '__iter__': BuiltinName(BuiltinFunction(get_iterator(first)))} + super(Tuple, self).__init__(tuple, attributes) + + def get_holding_objects(self): + return self.objects + + def _new_tuple(self, args): + return _create_builtin(args, get_tuple) + + +get_tuple = _create_builtin_getter(Tuple) +get_tuple_type = _create_builtin_type_getter(Tuple) + + +class Set(BuiltinClass): + + def __init__(self, holding=None): + self.holding = holding + collector = _AttributeCollector(set) + collector('__new__', function=self._new_set) + + self_methods = ['copy', 'difference', 'intersection', + 'symmetric_difference', 'union'] + for method in self_methods: + collector(method, function=self._self_get, parent=self) + collector('add', function=self._set_add, parent=self) + collector('update', function=self._self_set, parent=self) + collector('update', function=self._self_set, parent=self) + collector('symmetric_difference_update', function=self._self_set, + parent=self) + collector('difference_update', function=self._self_set, parent=self) + + collector('pop', function=self._set_get, parent=self) + collector('__iter__', function=self._iterator_get, parent=self) + super(Set, self).__init__(set, collector.attributes) + + def _new_set(self, args): + return _create_builtin(args, get_set) + + def _set_add(self, context): + if self.holding is not None: + return + holding = context.get_arguments(['self', 'value'])[1] + if holding is not None and holding != pyobjects.get_unknown(): + context.save_per_name(holding) + + def _self_set(self, context): + if self.holding is not None: + return + iterable = context.get_pyname('iterable') + holding = _infer_sequence_for_pyname(iterable) + if holding is not None and holding != pyobjects.get_unknown(): + context.save_per_name(holding) + + def _set_get(self, context): + if self.holding is not None: + return self.holding + return context.get_per_name() + + def _iterator_get(self, context): + return get_iterator(self._set_get(context)) + + def _self_get(self, context): + return get_list(self._set_get(context)) + + +get_set = _create_builtin_getter(Set) +get_set_type = _create_builtin_type_getter(Set) + + +class Str(BuiltinClass): + + def __init__(self): + self_object = pyobjects.PyObject(self) + collector = _AttributeCollector(str) + collector('__iter__', get_iterator(self_object), check_existence=False) + + self_methods = ['__getitem__', 'capitalize', 'center', + 'encode', 'expandtabs', 'join', 'ljust', + 'lower', 'lstrip', 'replace', 'rjust', 'rstrip', + 'strip', 'swapcase', 'title', 'translate', 'upper', + 'zfill'] + for method in self_methods: + collector(method, self_object, parent=self) + + py2_self_methods = ["__getslice__", "decode"] + for method in py2_self_methods: + try: + collector(method, self_object) + except AttributeError: + pass + + for method in ['rsplit', 'split', 'splitlines']: + collector(method, get_list(self_object), parent=self) + + super(Str, self).__init__(str, collector.attributes) + + def get_doc(self): + return str.__doc__ + + +get_str = _create_builtin_getter(Str) +get_str_type = _create_builtin_type_getter(Str) + + +class BuiltinName(pynames.PyName): + + def __init__(self, pyobject): + self.pyobject = pyobject + + def get_object(self): + return self.pyobject + + def get_definition_location(self): + return (None, None) + + +class Iterator(pyobjects.AbstractClass): + + def __init__(self, holding=None): + super(Iterator, self).__init__() + self.holding = holding + self.attributes = { + 'next': BuiltinName(BuiltinFunction(self.holding)), + '__iter__': BuiltinName(BuiltinFunction(self))} + + def get_attributes(self): + return self.attributes + + def get_returned_object(self, args): + return self.holding + +get_iterator = _create_builtin_getter(Iterator) + + +class Generator(pyobjects.AbstractClass): + + def __init__(self, holding=None): + super(Generator, self).__init__() + self.holding = holding + self.attributes = { + 'next': BuiltinName(BuiltinFunction(self.holding)), + '__iter__': BuiltinName(BuiltinFunction( + get_iterator(self.holding))), + 'close': BuiltinName(BuiltinFunction()), + 'send': BuiltinName(BuiltinFunction()), + 'throw': BuiltinName(BuiltinFunction())} + + def get_attributes(self): + return self.attributes + + def get_returned_object(self, args): + return self.holding + +get_generator = _create_builtin_getter(Generator) + + +class File(BuiltinClass): + + def __init__(self, filename=None, mode='r', *args): + self.filename = filename + self.mode = mode + self.args = args + str_object = get_str() + str_list = get_list(get_str()) + attributes = {} + + def add(name, returned=None, function=None): + builtin = getattr(io.TextIOBase, name, None) + attributes[name] = BuiltinName( + BuiltinFunction(returned=returned, function=function, + builtin=builtin)) + add('__iter__', get_iterator(str_object)) + add('__enter__', returned=self) + for method in ['next', 'read', 'readline', 'readlines']: + add(method, str_list) + for method in ['close', 'flush', 'lineno', 'isatty', 'seek', 'tell', + 'truncate', 'write', 'writelines']: + add(method) + super(File, self).__init__(open, attributes) + + +get_file = _create_builtin_getter(File) +get_file_type = _create_builtin_type_getter(File) + + +class Property(BuiltinClass): + + def __init__(self, fget=None, fset=None, fdel=None, fdoc=None): + self._fget = fget + self._fdoc = fdoc + attributes = { + 'fget': BuiltinName(BuiltinFunction()), + 'fset': BuiltinName(pynames.UnboundName()), + 'fdel': BuiltinName(pynames.UnboundName()), + '__new__': BuiltinName( + BuiltinFunction(function=_property_function))} + super(Property, self).__init__(property, attributes) + + def get_property_object(self, args): + if isinstance(self._fget, pyobjects.AbstractFunction): + return self._fget.get_returned_object(args) + + +def _property_function(args): + parameters = args.get_arguments(['fget', 'fset', 'fdel', 'fdoc']) + return pyobjects.PyObject(Property(parameters[0])) + + +class Lambda(pyobjects.AbstractFunction): + + def __init__(self, node, scope): + super(Lambda, self).__init__() + self.node = node + self.arguments = node.args + self.scope = scope + + def get_returned_object(self, args): + result = rope.base.evaluate.eval_node(self.scope, self.node.body) + if result is not None: + return result.get_object() + else: + return pyobjects.get_unknown() + + def get_module(self): + return self.parent.get_module() + + def get_scope(self): + return self.scope + + def get_kind(self): + return 'lambda' + + def get_ast(self): + return self.node + + def get_attributes(self): + return {} + + def get_name(self): + return 'lambda' + + def get_param_names(self, special_args=True): + result = [pycompat.get_ast_arg_arg(node) for node in self.arguments.args + if isinstance(node, pycompat.ast_arg_type)] + if self.arguments.vararg: + result.append('*' + pycompat.get_ast_arg_arg(self.arguments.vararg)) + if self.arguments.kwarg: + result.append('**' + pycompat.get_ast_arg_arg(self.arguments.kwarg)) + return result + + @property + def parent(self): + return self.scope.pyobject + + +class BuiltinObject(BuiltinClass): + + def __init__(self): + super(BuiltinObject, self).__init__(object, {}) + + +class BuiltinType(BuiltinClass): + + def __init__(self): + super(BuiltinType, self).__init__(type, {}) + + +def _infer_sequence_for_pyname(pyname): + if pyname is None: + return None + seq = pyname.get_object() + args = arguments.ObjectArguments([pyname]) + if '__iter__' in seq: + obj = seq['__iter__'].get_object() + if not isinstance(obj, pyobjects.AbstractFunction): + return None + iter = obj.get_returned_object(args) + if iter is not None and 'next' in iter: + holding = iter['next'].get_object().\ + get_returned_object(args) + return holding + + +def _create_builtin(args, creator): + passed = args.get_pynames(['sequence'])[0] + if passed is None: + holding = None + else: + holding = _infer_sequence_for_pyname(passed) + if holding is not None: + return creator(holding) + else: + return creator() + + +def _open_function(args): + return _create_builtin(args, get_file) + + +def _range_function(args): + return get_list() + + +def _reversed_function(args): + return _create_builtin(args, get_iterator) + + +def _sorted_function(args): + return _create_builtin(args, get_list) + + +def _super_function(args): + passed_class, passed_self = args.get_arguments(['type', 'self']) + if passed_self is None: + return passed_class + else: + #pyclass = passed_self.get_type() + pyclass = passed_class + if isinstance(pyclass, pyobjects.AbstractClass): + supers = pyclass.get_superclasses() + if supers: + return pyobjects.PyObject(supers[0]) + return passed_self + + +def _zip_function(args): + args = args.get_pynames(['sequence']) + objects = [] + for seq in args: + if seq is None: + holding = None + else: + holding = _infer_sequence_for_pyname(seq) + objects.append(holding) + tuple = get_tuple(*objects) + return get_list(tuple) + + +def _enumerate_function(args): + passed = args.get_pynames(['sequence'])[0] + if passed is None: + holding = None + else: + holding = _infer_sequence_for_pyname(passed) + tuple = get_tuple(None, holding) + return get_iterator(tuple) + + +def _iter_function(args): + passed = args.get_pynames(['sequence'])[0] + if passed is None: + holding = None + else: + holding = _infer_sequence_for_pyname(passed) + return get_iterator(holding) + + +def _input_function(args): + return get_str() + + +_initial_builtins = { + 'list': BuiltinName(get_list_type()), + 'dict': BuiltinName(get_dict_type()), + 'tuple': BuiltinName(get_tuple_type()), + 'set': BuiltinName(get_set_type()), + 'str': BuiltinName(get_str_type()), + 'file': BuiltinName(get_file_type()), + 'open': BuiltinName(BuiltinFunction(function=_open_function, + builtin=open)), + 'unicode': BuiltinName(get_str_type()), + 'range': BuiltinName(BuiltinFunction(function=_range_function, + builtin=range)), + 'reversed': BuiltinName(BuiltinFunction(function=_reversed_function, + builtin=reversed)), + 'sorted': BuiltinName(BuiltinFunction(function=_sorted_function, + builtin=sorted)), + 'super': BuiltinName(BuiltinFunction(function=_super_function, + builtin=super)), + 'property': BuiltinName(BuiltinFunction(function=_property_function, + builtin=property)), + 'zip': BuiltinName(BuiltinFunction(function=_zip_function, builtin=zip)), + 'enumerate': BuiltinName(BuiltinFunction(function=_enumerate_function, + builtin=enumerate)), + 'object': BuiltinName(BuiltinObject()), + 'type': BuiltinName(BuiltinType()), + 'iter': BuiltinName(BuiltinFunction(function=_iter_function, + builtin=iter)), + 'raw_input': BuiltinName(BuiltinFunction(function=_input_function, + builtin=raw_input)), +} + +builtins = BuiltinModule(pycompat.builtins.__name__, initial=_initial_builtins) diff --git a/venv/Lib/site-packages/rope/base/change.py b/venv/Lib/site-packages/rope/base/change.py new file mode 100644 index 0000000000000000000000000000000000000000..fe2ebf435adb36e28509d56f82ca5a707f78df1a --- /dev/null +++ b/venv/Lib/site-packages/rope/base/change.py @@ -0,0 +1,450 @@ +import datetime +import difflib +import os +import time + +import rope.base.fscommands +from rope.base import taskhandle, exceptions, utils + + +class Change(object): + """The base class for changes + + Rope refactorings return `Change` objects. They can be previewed, + committed or undone. + """ + + def do(self, job_set=None): + """Perform the change + + .. note:: Do use this directly. Use `Project.do()` instead. + """ + + def undo(self, job_set=None): + """Perform the change + + .. note:: Do use this directly. Use `History.undo()` instead. + """ + + def get_description(self): + """Return the description of this change + + This can be used for previewing the changes. + """ + return str(self) + + def get_changed_resources(self): + """Return the list of resources that will be changed""" + return [] + + @property + @utils.saveit + def _operations(self): + return _ResourceOperations(self.resource.project) + + +class ChangeSet(Change): + """A collection of `Change` objects + + This class holds a collection of changes. This class provides + these fields: + + * `changes`: the list of changes + * `description`: the goal of these changes + """ + + def __init__(self, description, timestamp=None): + self.changes = [] + self.description = description + self.time = timestamp + + def do(self, job_set=taskhandle.NullJobSet()): + try: + done = [] + for change in self.changes: + change.do(job_set) + done.append(change) + self.time = time.time() + except Exception: + for change in done: + change.undo() + raise + + def undo(self, job_set=taskhandle.NullJobSet()): + try: + done = [] + for change in reversed(self.changes): + change.undo(job_set) + done.append(change) + except Exception: + for change in done: + change.do() + raise + + def add_change(self, change): + self.changes.append(change) + + def get_description(self): + result = [str(self) + ':\n\n\n'] + for change in self.changes: + result.append(change.get_description()) + result.append('\n') + return ''.join(result) + + def __str__(self): + if self.time is not None: + date = datetime.datetime.fromtimestamp(self.time) + if date.date() == datetime.date.today(): + string_date = 'today' + elif date.date() == (datetime.date.today() - + datetime.timedelta(1)): + string_date = 'yesterday' + elif date.year == datetime.date.today().year: + string_date = date.strftime('%b %d') + else: + string_date = date.strftime('%d %b, %Y') + string_time = date.strftime('%H:%M:%S') + string_time = '%s %s ' % (string_date, string_time) + return self.description + ' - ' + string_time + return self.description + + def get_changed_resources(self): + result = set() + for change in self.changes: + result.update(change.get_changed_resources()) + return result + + +def _handle_job_set(function): + """A decorator for handling `taskhandle.JobSet`\s + + A decorator for handling `taskhandle.JobSet`\s for `do` and `undo` + methods of `Change`\s. + """ + def call(self, job_set=taskhandle.NullJobSet()): + job_set.started_job(str(self)) + function(self) + job_set.finished_job() + return call + + +class ChangeContents(Change): + """A class to change the contents of a file + + Fields: + + * `resource`: The `rope.base.resources.File` to change + * `new_contents`: What to write in the file + """ + + def __init__(self, resource, new_contents, old_contents=None): + self.resource = resource + # IDEA: Only saving diffs; possible problems when undo/redoing + self.new_contents = new_contents + self.old_contents = old_contents + + @_handle_job_set + def do(self): + if self.old_contents is None: + self.old_contents = self.resource.read() + self._operations.write_file(self.resource, self.new_contents) + + @_handle_job_set + def undo(self): + if self.old_contents is None: + raise exceptions.HistoryError( + 'Undoing a change that is not performed yet!') + self._operations.write_file(self.resource, self.old_contents) + + def __str__(self): + return 'Change <%s>' % self.resource.path + + def get_description(self): + new = self.new_contents + old = self.old_contents + if old is None: + if self.resource.exists(): + old = self.resource.read() + else: + old = '' + result = difflib.unified_diff( + old.splitlines(True), new.splitlines(True), + 'a/' + self.resource.path, 'b/' + self.resource.path) + return ''.join(list(result)) + + def get_changed_resources(self): + return [self.resource] + + +class MoveResource(Change): + """Move a resource to a new location + + Fields: + + * `resource`: The `rope.base.resources.Resource` to move + * `new_resource`: The destination for move; It is the moved + resource not the folder containing that resource. + """ + + def __init__(self, resource, new_location, exact=False): + self.project = resource.project + self.resource = resource + if not exact: + new_location = _get_destination_for_move(resource, new_location) + if resource.is_folder(): + self.new_resource = self.project.get_folder(new_location) + else: + self.new_resource = self.project.get_file(new_location) + + @_handle_job_set + def do(self): + self._operations.move(self.resource, self.new_resource) + + @_handle_job_set + def undo(self): + self._operations.move(self.new_resource, self.resource) + + def __str__(self): + return 'Move <%s>' % self.resource.path + + def get_description(self): + return 'rename from %s\nrename to %s' % (self.resource.path, + self.new_resource.path) + + def get_changed_resources(self): + return [self.resource, self.new_resource] + + +class CreateResource(Change): + """A class to create a resource + + Fields: + + * `resource`: The resource to create + """ + + def __init__(self, resource): + self.resource = resource + + @_handle_job_set + def do(self): + self._operations.create(self.resource) + + @_handle_job_set + def undo(self): + self._operations.remove(self.resource) + + def __str__(self): + return 'Create Resource <%s>' % (self.resource.path) + + def get_description(self): + return 'new file %s' % (self.resource.path) + + def get_changed_resources(self): + return [self.resource] + + def _get_child_path(self, parent, name): + if parent.path == '': + return name + else: + return parent.path + '/' + name + + +class CreateFolder(CreateResource): + """A class to create a folder + + See docs for `CreateResource`. + """ + + def __init__(self, parent, name): + resource = parent.project.get_folder( + self._get_child_path(parent, name)) + super(CreateFolder, self).__init__(resource) + + +class CreateFile(CreateResource): + """A class to create a file + + See docs for `CreateResource`. + """ + + def __init__(self, parent, name): + resource = parent.project.get_file(self._get_child_path(parent, name)) + super(CreateFile, self).__init__(resource) + + +class RemoveResource(Change): + """A class to remove a resource + + Fields: + + * `resource`: The resource to be removed + """ + + def __init__(self, resource): + self.resource = resource + + @_handle_job_set + def do(self): + self._operations.remove(self.resource) + + # TODO: Undoing remove operations + @_handle_job_set + def undo(self): + raise NotImplementedError( + 'Undoing `RemoveResource` is not implemented yet.') + + def __str__(self): + return 'Remove <%s>' % (self.resource.path) + + def get_changed_resources(self): + return [self.resource] + + +def count_changes(change): + """Counts the number of basic changes a `Change` will make""" + if isinstance(change, ChangeSet): + result = 0 + for child in change.changes: + result += count_changes(child) + return result + return 1 + + +def create_job_set(task_handle, change): + return task_handle.create_jobset(str(change), count_changes(change)) + + +class _ResourceOperations(object): + + def __init__(self, project): + self.project = project + self.fscommands = project.fscommands + self.direct_commands = rope.base.fscommands.FileSystemCommands() + + def _get_fscommands(self, resource): + if self.project.is_ignored(resource): + return self.direct_commands + return self.fscommands + + def write_file(self, resource, contents): + data = rope.base.fscommands.unicode_to_file_data(contents) + fscommands = self._get_fscommands(resource) + fscommands.write(resource.real_path, data) + for observer in list(self.project.observers): + observer.resource_changed(resource) + + def move(self, resource, new_resource): + fscommands = self._get_fscommands(resource) + fscommands.move(resource.real_path, new_resource.real_path) + for observer in list(self.project.observers): + observer.resource_moved(resource, new_resource) + + def create(self, resource): + if resource.is_folder(): + self._create_resource(resource.path, kind='folder') + else: + self._create_resource(resource.path) + for observer in list(self.project.observers): + observer.resource_created(resource) + + def remove(self, resource): + fscommands = self._get_fscommands(resource) + fscommands.remove(resource.real_path) + for observer in list(self.project.observers): + observer.resource_removed(resource) + + def _create_resource(self, file_name, kind='file'): + resource_path = self.project._get_resource_path(file_name) + if os.path.exists(resource_path): + raise exceptions.RopeError('Resource <%s> already exists' + % resource_path) + resource = self.project.get_file(file_name) + if not resource.parent.exists(): + raise exceptions.ResourceNotFoundError( + 'Parent folder of <%s> does not exist' % resource.path) + fscommands = self._get_fscommands(resource) + try: + if kind == 'file': + fscommands.create_file(resource_path) + else: + fscommands.create_folder(resource_path) + except IOError as e: + raise exceptions.RopeError(e) + + +def _get_destination_for_move(resource, destination): + dest_path = resource.project._get_resource_path(destination) + if os.path.isdir(dest_path): + if destination != '': + return destination + '/' + resource.name + else: + return resource.name + return destination + + +class ChangeToData(object): + + def convertChangeSet(self, change): + description = change.description + changes = [] + for child in change.changes: + changes.append(self(child)) + return (description, changes, change.time) + + def convertChangeContents(self, change): + return (change.resource.path, change.new_contents, change.old_contents) + + def convertMoveResource(self, change): + return (change.resource.path, change.new_resource.path) + + def convertCreateResource(self, change): + return (change.resource.path, change.resource.is_folder()) + + def convertRemoveResource(self, change): + return (change.resource.path, change.resource.is_folder()) + + def __call__(self, change): + change_type = type(change) + if change_type in (CreateFolder, CreateFile): + change_type = CreateResource + method = getattr(self, 'convert' + change_type.__name__) + return (change_type.__name__, method(change)) + + +class DataToChange(object): + + def __init__(self, project): + self.project = project + + def makeChangeSet(self, description, changes, time=None): + result = ChangeSet(description, time) + for child in changes: + result.add_change(self(child)) + return result + + def makeChangeContents(self, path, new_contents, old_contents): + resource = self.project.get_file(path) + return ChangeContents(resource, new_contents, old_contents) + + def makeMoveResource(self, old_path, new_path): + resource = self.project.get_file(old_path) + return MoveResource(resource, new_path, exact=True) + + def makeCreateResource(self, path, is_folder): + if is_folder: + resource = self.project.get_folder(path) + else: + resource = self.project.get_file(path) + return CreateResource(resource) + + def makeRemoveResource(self, path, is_folder): + if is_folder: + resource = self.project.get_folder(path) + else: + resource = self.project.get_file(path) + return RemoveResource(resource) + + def __call__(self, data): + method = getattr(self, 'make' + data[0]) + return method(*data[1]) diff --git a/venv/Lib/site-packages/rope/base/codeanalyze.py b/venv/Lib/site-packages/rope/base/codeanalyze.py new file mode 100644 index 0000000000000000000000000000000000000000..1704e9ade971be894a459f54a441e6c397783481 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/codeanalyze.py @@ -0,0 +1,362 @@ +import bisect +import re +import token +import tokenize + + +class ChangeCollector(object): + + def __init__(self, text): + self.text = text + self.changes = [] + + def add_change(self, start, end, new_text=None): + if new_text is None: + new_text = self.text[start:end] + self.changes.append((start, end, new_text)) + + def get_changed(self): + if not self.changes: + return None + + self.changes.sort(key=lambda x: x[:2]) + pieces = [] + last_changed = 0 + for change in self.changes: + start, end, text = change + pieces.append(self.text[last_changed:start] + text) + last_changed = end + if last_changed < len(self.text): + pieces.append(self.text[last_changed:]) + result = ''.join(pieces) + if result != self.text: + return result + + +class SourceLinesAdapter(object): + """Adapts source to Lines interface + + Note: The creation of this class is expensive. + """ + + def __init__(self, source_code): + self.code = source_code + self.starts = None + self._initialize_line_starts() + + def _initialize_line_starts(self): + self.starts = [] + self.starts.append(0) + try: + i = 0 + while True: + i = self.code.index('\n', i) + 1 + self.starts.append(i) + except ValueError: + pass + self.starts.append(len(self.code) + 1) + + def get_line(self, lineno): + return self.code[self.starts[lineno - 1]: + self.starts[lineno] - 1] + + def length(self): + return len(self.starts) - 1 + + def get_line_number(self, offset): + return bisect.bisect(self.starts, offset) + + def get_line_start(self, lineno): + return self.starts[lineno - 1] + + def get_line_end(self, lineno): + return self.starts[lineno] - 1 + + +class ArrayLinesAdapter(object): + + def __init__(self, lines): + self.lines = lines + + def get_line(self, line_number): + return self.lines[line_number - 1] + + def length(self): + return len(self.lines) + + +class LinesToReadline(object): + + def __init__(self, lines, start): + self.lines = lines + self.current = start + + def readline(self): + if self.current <= self.lines.length(): + self.current += 1 + return self.lines.get_line(self.current - 1) + '\n' + return '' + + def __call__(self): + return self.readline() + + +class _CustomGenerator(object): + + def __init__(self, lines): + self.lines = lines + self.in_string = '' + self.open_count = 0 + self.continuation = False + + def __call__(self): + size = self.lines.length() + result = [] + i = 1 + while i <= size: + while i <= size and not self.lines.get_line(i).strip(): + i += 1 + if i <= size: + start = i + while True: + line = self.lines.get_line(i) + self._analyze_line(line) + if not (self.continuation or self.open_count or + self.in_string) or i == size: + break + i += 1 + result.append((start, i)) + i += 1 + return result + + # Matches all backslashes before the token, to detect escaped quotes + _main_tokens = re.compile(r'(\\*)((\'\'\'|"""|\'|")|#|\[|\]|\{|\}|\(|\))') + + def _analyze_line(self, line): + token = None + for match in self._main_tokens.finditer(line): + prefix = match.group(1) + token = match.group(2) + # Skip any tokens which are escaped + if len(prefix) % 2 == 1: + continue + if token in ["'''", '"""', "'", '"']: + if not self.in_string: + self.in_string = token + elif self.in_string == token: + self.in_string = '' + if self.in_string: + continue + if token == '#': + break + if token in '([{': + self.open_count += 1 + elif token in ')]}': + self.open_count -= 1 + if line and token != '#' and line.endswith('\\'): + self.continuation = True + else: + self.continuation = False + + +def custom_generator(lines): + return _CustomGenerator(lines)() + + +class LogicalLineFinder(object): + + def __init__(self, lines): + self.lines = lines + + def logical_line_in(self, line_number): + indents = count_line_indents(self.lines.get_line(line_number)) + tries = 0 + while True: + block_start = get_block_start(self.lines, line_number, indents) + try: + return self._block_logical_line(block_start, line_number) + except IndentationError as e: + tries += 1 + if tries == 5: + raise e + lineno = e.lineno + block_start - 1 + indents = count_line_indents(self.lines.get_line(lineno)) + + def generate_starts(self, start_line=1, end_line=None): + for start, end in self.generate_regions(start_line, end_line): + yield start + + def generate_regions(self, start_line=1, end_line=None): + # XXX: `block_start` should be at a better position! + block_start = 1 + readline = LinesToReadline(self.lines, block_start) + try: + for start, end in self._logical_lines(readline): + real_start = start + block_start - 1 + real_start = self._first_non_blank(real_start) + if end_line is not None and real_start >= end_line: + break + real_end = end + block_start - 1 + if real_start >= start_line: + yield (real_start, real_end) + except tokenize.TokenError: + pass + + def _block_logical_line(self, block_start, line_number): + readline = LinesToReadline(self.lines, block_start) + shifted = line_number - block_start + 1 + region = self._calculate_logical(readline, shifted) + start = self._first_non_blank(region[0] + block_start - 1) + if region[1] is None: + end = self.lines.length() + else: + end = region[1] + block_start - 1 + return start, end + + def _calculate_logical(self, readline, line_number): + last_end = 1 + try: + for start, end in self._logical_lines(readline): + if line_number <= end: + return (start, end) + last_end = end + 1 + except tokenize.TokenError as e: + current = e.args[1][0] + return (last_end, max(last_end, current - 1)) + return (last_end, None) + + def _logical_lines(self, readline): + last_end = 1 + for current_token in tokenize.generate_tokens(readline): + current = current_token[2][0] + if current_token[0] == token.NEWLINE: + yield (last_end, current) + last_end = current + 1 + + def _first_non_blank(self, line_number): + current = line_number + while current < self.lines.length(): + line = self.lines.get_line(current).strip() + if line and not line.startswith('#'): + return current + current += 1 + return current + + +def tokenizer_generator(lines): + return LogicalLineFinder(lines).generate_regions() + + +class CachingLogicalLineFinder(object): + + def __init__(self, lines, generate=custom_generator): + self.lines = lines + self._generate = generate + + _starts = None + + @property + def starts(self): + if self._starts is None: + self._init_logicals() + return self._starts + + _ends = None + + @property + def ends(self): + if self._ends is None: + self._init_logicals() + return self._ends + + def _init_logicals(self): + """Should initialize _starts and _ends attributes""" + size = self.lines.length() + 1 + self._starts = [None] * size + self._ends = [None] * size + for start, end in self._generate(self.lines): + self._starts[start] = True + self._ends[end] = True + + def logical_line_in(self, line_number): + start = line_number + while start > 0 and not self.starts[start]: + start -= 1 + if start == 0: + try: + start = self.starts.index(True, line_number) + except ValueError: + return (line_number, line_number) + return (start, self.ends.index(True, start)) + + def generate_starts(self, start_line=1, end_line=None): + if end_line is None: + end_line = self.lines.length() + for index in range(start_line, end_line): + if self.starts[index]: + yield index + + +def get_block_start(lines, lineno, maximum_indents=80): + """Approximate block start""" + pattern = get_block_start_patterns() + for i in range(lineno, 0, -1): + match = pattern.search(lines.get_line(i)) + if match is not None and \ + count_line_indents(lines.get_line(i)) <= maximum_indents: + striped = match.string.lstrip() + # Maybe we're in a list comprehension or generator expression + if i > 1 and striped.startswith('if') or striped.startswith('for'): + bracs = 0 + for j in range(i, min(i + 5, lines.length() + 1)): + for c in lines.get_line(j): + if c == '#': + break + if c in '[(': + bracs += 1 + if c in ')]': + bracs -= 1 + if bracs < 0: + break + if bracs < 0: + break + if bracs < 0: + continue + return i + return 1 + + +_block_start_pattern = None + + +def get_block_start_patterns(): + global _block_start_pattern + if not _block_start_pattern: + pattern = '^\\s*(((def|class|if|elif|except|for|while|with)\\s)|'\ + '((try|else|finally|except)\\s*:))' + _block_start_pattern = re.compile(pattern, re.M) + return _block_start_pattern + + +def count_line_indents(line): + indents = 0 + for char in line: + if char == ' ': + indents += 1 + elif char == '\t': + indents += 8 + else: + return indents + return 0 + + +def get_string_pattern(): + start = r'(\b[uU]?[rR]?)?' + longstr = r'%s"""(\\.|"(?!"")|\\\n|[^"\\])*"""' % start + shortstr = r'%s"(\\.|\\\n|[^"\\])*"' % start + return '|'.join([longstr, longstr.replace('"', "'"), + shortstr, shortstr.replace('"', "'")]) + + +def get_comment_pattern(): + return r'#[^\n]*' diff --git a/venv/Lib/site-packages/rope/base/default_config.py b/venv/Lib/site-packages/rope/base/default_config.py new file mode 100644 index 0000000000000000000000000000000000000000..dee2d1ae9a6be9cf0248130b6c6b9e2668052079 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/default_config.py @@ -0,0 +1,114 @@ +# The default ``config.py`` +# flake8: noqa + + +def set_prefs(prefs): + """This function is called before opening the project""" + + # Specify which files and folders to ignore in the project. + # Changes to ignored resources are not added to the history and + # VCSs. Also they are not returned in `Project.get_files()`. + # Note that ``?`` and ``*`` match all characters but slashes. + # '*.pyc': matches 'test.pyc' and 'pkg/test.pyc' + # 'mod*.pyc': matches 'test/mod1.pyc' but not 'mod/1.pyc' + # '.svn': matches 'pkg/.svn' and all of its children + # 'build/*.o': matches 'build/lib.o' but not 'build/sub/lib.o' + # 'build//*.o': matches 'build/lib.o' and 'build/sub/lib.o' + prefs['ignored_resources'] = ['*.pyc', '*~', '.ropeproject', + '.hg', '.svn', '_svn', '.git', '.tox'] + + # Specifies which files should be considered python files. It is + # useful when you have scripts inside your project. Only files + # ending with ``.py`` are considered to be python files by + # default. + # prefs['python_files'] = ['*.py'] + + # Custom source folders: By default rope searches the project + # for finding source folders (folders that should be searched + # for finding modules). You can add paths to that list. Note + # that rope guesses project source folders correctly most of the + # time; use this if you have any problems. + # The folders should be relative to project root and use '/' for + # separating folders regardless of the platform rope is running on. + # 'src/my_source_folder' for instance. + # prefs.add('source_folders', 'src') + + # You can extend python path for looking up modules + # prefs.add('python_path', '~/python/') + + # Should rope save object information or not. + prefs['save_objectdb'] = True + prefs['compress_objectdb'] = False + + # If `True`, rope analyzes each module when it is being saved. + prefs['automatic_soa'] = True + # The depth of calls to follow in static object analysis + prefs['soa_followed_calls'] = 0 + + # If `False` when running modules or unit tests "dynamic object + # analysis" is turned off. This makes them much faster. + prefs['perform_doa'] = True + + # Rope can check the validity of its object DB when running. + prefs['validate_objectdb'] = True + + # How many undos to hold? + prefs['max_history_items'] = 32 + + # Shows whether to save history across sessions. + prefs['save_history'] = True + prefs['compress_history'] = False + + # Set the number spaces used for indenting. According to + # :PEP:`8`, it is best to use 4 spaces. Since most of rope's + # unit-tests use 4 spaces it is more reliable, too. + prefs['indent_size'] = 4 + + # Builtin and c-extension modules that are allowed to be imported + # and inspected by rope. + prefs['extension_modules'] = [] + + # Add all standard c-extensions to extension_modules list. + prefs['import_dynload_stdmods'] = True + + # If `True` modules with syntax errors are considered to be empty. + # The default value is `False`; When `False` syntax errors raise + # `rope.base.exceptions.ModuleSyntaxError` exception. + prefs['ignore_syntax_errors'] = False + + # If `True`, rope ignores unresolvable imports. Otherwise, they + # appear in the importing namespace. + prefs['ignore_bad_imports'] = False + + # If `True`, rope will insert new module imports as + # `from <package> import <module>` by default. + prefs['prefer_module_from_imports'] = False + + # If `True`, rope will transform a comma list of imports into + # multiple separate import statements when organizing + # imports. + prefs['split_imports'] = False + + # If `True`, rope will remove all top-level import statements and + # reinsert them at the top of the module when making changes. + prefs['pull_imports_to_top'] = True + + # If `True`, rope will sort imports alphabetically by module name instead + # of alphabetically by import statement, with from imports after normal + # imports. + prefs['sort_imports_alphabetically'] = False + + # Location of implementation of + # rope.base.oi.type_hinting.interfaces.ITypeHintingFactory In general + # case, you don't have to change this value, unless you're an rope expert. + # Change this value to inject you own implementations of interfaces + # listed in module rope.base.oi.type_hinting.providers.interfaces + # For example, you can add you own providers for Django Models, or disable + # the search type-hinting in a class hierarchy, etc. + prefs['type_hinting_factory'] = ( + 'rope.base.oi.type_hinting.factory.default_type_hinting_factory') + + +def project_opened(project): + """This function is called after opening the project""" + # Do whatever you like here! diff --git a/venv/Lib/site-packages/rope/base/evaluate.py b/venv/Lib/site-packages/rope/base/evaluate.py new file mode 100644 index 0000000000000000000000000000000000000000..ce38bd025b52fd0890355a0836e577198fa09698 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/evaluate.py @@ -0,0 +1,333 @@ +import rope.base.builtins +import rope.base.pynames +import rope.base.pyobjects +from rope.base import ast, astutils, exceptions, pyobjects, arguments, worder +from rope.base.utils import pycompat + + +BadIdentifierError = exceptions.BadIdentifierError + + +def eval_location(pymodule, offset): + """Find the pyname at the offset""" + return eval_location2(pymodule, offset)[1] + + +def eval_location2(pymodule, offset): + """Find the primary and pyname at offset""" + pyname_finder = ScopeNameFinder(pymodule) + return pyname_finder.get_primary_and_pyname_at(offset) + + +def eval_node(scope, node): + """Evaluate a `ast.AST` node and return a PyName + + Return `None` if the expression cannot be evaluated. + """ + return eval_node2(scope, node)[1] + + +def eval_node2(scope, node): + evaluator = StatementEvaluator(scope) + ast.walk(node, evaluator) + return evaluator.old_result, evaluator.result + + +def eval_str(holding_scope, name): + return eval_str2(holding_scope, name)[1] + + +def eval_str2(holding_scope, name): + try: + # parenthesizing for handling cases like 'a_var.\nattr' + node = ast.parse('(%s)' % name) + except SyntaxError: + raise BadIdentifierError( + 'Not a resolvable python identifier selected.') + return eval_node2(holding_scope, node) + + +class ScopeNameFinder(object): + + def __init__(self, pymodule): + self.module_scope = pymodule.get_scope() + self.lines = pymodule.lines + self.worder = worder.Worder(pymodule.source_code, True) + + def _is_defined_in_class_body(self, holding_scope, offset, lineno): + if lineno == holding_scope.get_start() and \ + holding_scope.parent is not None and \ + holding_scope.parent.get_kind() == 'Class' and \ + self.worder.is_a_class_or_function_name_in_header(offset): + return True + if lineno != holding_scope.get_start() and \ + holding_scope.get_kind() == 'Class' and \ + self.worder.is_name_assigned_in_class_body(offset): + return True + return False + + def _is_function_name_in_function_header(self, scope, offset, lineno): + if scope.get_start() <= lineno <= scope.get_body_start() and \ + scope.get_kind() == 'Function' and \ + self.worder.is_a_class_or_function_name_in_header(offset): + return True + return False + + def get_pyname_at(self, offset): + return self.get_primary_and_pyname_at(offset)[1] + + def get_primary_and_pyname_at(self, offset): + lineno = self.lines.get_line_number(offset) + holding_scope = self.module_scope.get_inner_scope_for_line(lineno) + # function keyword parameter + if self.worder.is_function_keyword_parameter(offset): + keyword_name = self.worder.get_word_at(offset) + pyobject = self.get_enclosing_function(offset) + if isinstance(pyobject, pyobjects.PyFunction): + return (None, + pyobject.get_parameters().get(keyword_name, None)) + # class body + if self._is_defined_in_class_body(holding_scope, offset, lineno): + class_scope = holding_scope + if lineno == holding_scope.get_start(): + class_scope = holding_scope.parent + name = self.worder.get_primary_at(offset).strip() + try: + return (None, class_scope.pyobject[name]) + except rope.base.exceptions.AttributeNotFoundError: + return (None, None) + # function header + if self._is_function_name_in_function_header(holding_scope, + offset, lineno): + name = self.worder.get_primary_at(offset).strip() + return (None, holding_scope.parent[name]) + # module in a from statement or an imported name that is aliased + if (self.worder.is_from_statement_module(offset) or + self.worder.is_import_statement_aliased_module(offset)): + module = self.worder.get_primary_at(offset) + module_pyname = self._find_module(module) + return (None, module_pyname) + if self.worder.is_from_aliased(offset): + name = self.worder.get_from_aliased(offset) + else: + name = self.worder.get_primary_at(offset) + return eval_str2(holding_scope, name) + + def get_enclosing_function(self, offset): + function_parens = self.worder.find_parens_start_from_inside(offset) + try: + function_pyname = self.get_pyname_at(function_parens - 1) + except BadIdentifierError: + function_pyname = None + if function_pyname is not None: + pyobject = function_pyname.get_object() + if isinstance(pyobject, pyobjects.AbstractFunction): + return pyobject + elif isinstance(pyobject, pyobjects.AbstractClass) and \ + '__init__' in pyobject: + return pyobject['__init__'].get_object() + elif '__call__' in pyobject: + return pyobject['__call__'].get_object() + return None + + def _find_module(self, module_name): + dots = 0 + while module_name[dots] == '.': + dots += 1 + return rope.base.pynames.ImportedModule( + self.module_scope.pyobject, module_name[dots:], dots) + + +class StatementEvaluator(object): + + def __init__(self, scope): + self.scope = scope + self.result = None + self.old_result = None + + def _Name(self, node): + self.result = self.scope.lookup(node.id) + + def _Attribute(self, node): + pyname = eval_node(self.scope, node.value) + if pyname is None: + pyname = rope.base.pynames.UnboundName() + self.old_result = pyname + if pyname.get_object() != rope.base.pyobjects.get_unknown(): + try: + self.result = pyname.get_object()[node.attr] + except exceptions.AttributeNotFoundError: + self.result = None + + def _Call(self, node): + primary, pyobject = self._get_primary_and_object_for_node(node.func) + if pyobject is None: + return + + def _get_returned(pyobject): + args = arguments.create_arguments(primary, pyobject, + node, self.scope) + return pyobject.get_returned_object(args) + if isinstance(pyobject, rope.base.pyobjects.AbstractClass): + result = None + if '__new__' in pyobject: + new_function = pyobject['__new__'].get_object() + result = _get_returned(new_function) + if result is None or \ + result == rope.base.pyobjects.get_unknown(): + result = rope.base.pyobjects.PyObject(pyobject) + self.result = rope.base.pynames.UnboundName(pyobject=result) + return + + pyfunction = None + if isinstance(pyobject, rope.base.pyobjects.AbstractFunction): + pyfunction = pyobject + elif '__call__' in pyobject: + pyfunction = pyobject['__call__'].get_object() + if pyfunction is not None: + self.result = rope.base.pynames.UnboundName( + pyobject=_get_returned(pyfunction)) + + def _Str(self, node): + self.result = rope.base.pynames.UnboundName( + pyobject=rope.base.builtins.get_str()) + + def _Num(self, node): + type_name = type(node.n).__name__ + self.result = self._get_builtin_name(type_name) + + def _get_builtin_name(self, type_name): + pytype = rope.base.builtins.builtins[type_name].get_object() + return rope.base.pynames.UnboundName( + rope.base.pyobjects.PyObject(pytype)) + + def _BinOp(self, node): + self.result = rope.base.pynames.UnboundName( + self._get_object_for_node(node.left)) + + def _BoolOp(self, node): + pyobject = self._get_object_for_node(node.values[0]) + if pyobject is None: + pyobject = self._get_object_for_node(node.values[1]) + self.result = rope.base.pynames.UnboundName(pyobject) + + def _Repr(self, node): + self.result = self._get_builtin_name('str') + + def _UnaryOp(self, node): + self.result = rope.base.pynames.UnboundName( + self._get_object_for_node(node.operand)) + + def _Compare(self, node): + self.result = self._get_builtin_name('bool') + + def _Dict(self, node): + keys = None + values = None + if node.keys: + keys = self._get_object_for_node(node.keys[0]) + values = self._get_object_for_node(node.values[0]) + self.result = rope.base.pynames.UnboundName( + pyobject=rope.base.builtins.get_dict(keys, values)) + + def _List(self, node): + holding = None + if node.elts: + holding = self._get_object_for_node(node.elts[0]) + self.result = rope.base.pynames.UnboundName( + pyobject=rope.base.builtins.get_list(holding)) + + def _ListComp(self, node): + pyobject = self._what_does_comprehension_hold(node) + self.result = rope.base.pynames.UnboundName( + pyobject=rope.base.builtins.get_list(pyobject)) + + def _GeneratorExp(self, node): + pyobject = self._what_does_comprehension_hold(node) + self.result = rope.base.pynames.UnboundName( + pyobject=rope.base.builtins.get_iterator(pyobject)) + + def _what_does_comprehension_hold(self, node): + scope = self._make_comprehension_scope(node) + pyname = eval_node(scope, node.elt) + return pyname.get_object() if pyname is not None else None + + def _make_comprehension_scope(self, node): + scope = self.scope + module = scope.pyobject.get_module() + names = {} + for comp in node.generators: + new_names = _get_evaluated_names(comp.target, comp.iter, module, + '.__iter__().next()', node.lineno) + names.update(new_names) + return rope.base.pyscopes.TemporaryScope(scope.pycore, scope, names) + + def _Tuple(self, node): + objects = [] + if len(node.elts) < 4: + for stmt in node.elts: + pyobject = self._get_object_for_node(stmt) + objects.append(pyobject) + else: + objects.append(self._get_object_for_node(node.elts[0])) + self.result = rope.base.pynames.UnboundName( + pyobject=rope.base.builtins.get_tuple(*objects)) + + def _get_object_for_node(self, stmt): + pyname = eval_node(self.scope, stmt) + pyobject = None + if pyname is not None: + pyobject = pyname.get_object() + return pyobject + + def _get_primary_and_object_for_node(self, stmt): + primary, pyname = eval_node2(self.scope, stmt) + pyobject = None + if pyname is not None: + pyobject = pyname.get_object() + return primary, pyobject + + def _Subscript(self, node): + if isinstance(node.slice, ast.Index): + self._call_function(node.value, '__getitem__', + [node.slice.value]) + elif isinstance(node.slice, ast.Slice): + self._call_function(node.value, '__getitem__', + [node.slice]) + + def _Slice(self, node): + self.result = self._get_builtin_name('slice') + + def _call_function(self, node, function_name, other_args=None): + pyname = eval_node(self.scope, node) + if pyname is not None: + pyobject = pyname.get_object() + else: + return + if function_name in pyobject: + called = pyobject[function_name].get_object() + if not called or \ + not isinstance(called, pyobjects.AbstractFunction): + return + args = [node] + if other_args: + args += other_args + arguments_ = arguments.Arguments(args, self.scope) + self.result = rope.base.pynames.UnboundName( + pyobject=called.get_returned_object(arguments_)) + + def _Lambda(self, node): + self.result = rope.base.pynames.UnboundName( + pyobject=rope.base.builtins.Lambda(node, self.scope)) + + +def _get_evaluated_names(targets, assigned, module, evaluation, lineno): + result = {} + for name, levels in astutils.get_name_levels(targets): + assignment = rope.base.pynames.AssignmentValue(assigned, levels, + evaluation) + # XXX: this module should not access `rope.base.pynamesdef`! + pyname = rope.base.pynamesdef.AssignedName(lineno, module) + pyname.assignments.append(assignment) + result[name] = pyname + return result diff --git a/venv/Lib/site-packages/rope/base/exceptions.py b/venv/Lib/site-packages/rope/base/exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..d161c89ed688cd463434c6882566f12299853cf4 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/exceptions.py @@ -0,0 +1,61 @@ +class RopeError(Exception): + """Base exception for rope""" + + +class ResourceNotFoundError(RopeError): + """Resource not found exception""" + + +class RefactoringError(RopeError): + """Errors for performing a refactoring""" + + +class InterruptedTaskError(RopeError): + """The task has been interrupted""" + + +class HistoryError(RopeError): + """Errors for history undo/redo operations""" + + +class ModuleNotFoundError(RopeError): + """Module not found exception""" + + +class AttributeNotFoundError(RopeError): + """Attribute not found exception""" + + +class NameNotFoundError(RopeError): + """Name not found exception""" + + +class BadIdentifierError(RopeError): + """The name cannot be resolved""" + + +class ModuleSyntaxError(RopeError): + """Module has syntax errors + + The `filename` and `lineno` fields indicate where the error has + occurred. + + """ + + def __init__(self, filename, lineno, message): + self.filename = filename + self.lineno = lineno + self.message_ = message + super(ModuleSyntaxError, self).__init__( + 'Syntax error in file <%s> line <%s>: %s' % + (filename, lineno, message)) + + +class ModuleDecodeError(RopeError): + """Cannot decode module""" + + def __init__(self, filename, message): + self.filename = filename + self.message_ = message + super(ModuleDecodeError, self).__init__( + 'Cannot decode file <%s>: %s' % (filename, message)) diff --git a/venv/Lib/site-packages/rope/base/fscommands.py b/venv/Lib/site-packages/rope/base/fscommands.py new file mode 100644 index 0000000000000000000000000000000000000000..3564ed919c9cfb40b50806b43940c8f8240d4135 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/fscommands.py @@ -0,0 +1,288 @@ +"""Project file system commands. + +This modules implements file system operations used by rope. Different +version control systems can be supported by implementing the interface +provided by `FileSystemCommands` class. See `SubversionCommands` and +`MercurialCommands` for example. + +""" +import os +import shutil +import subprocess + +import rope.base.utils.pycompat as pycompat + +try: + unicode +except NameError: + unicode = str + +def create_fscommands(root): + dirlist = os.listdir(root) + commands = {'.hg': MercurialCommands, + '.svn': SubversionCommands, + '.git': GITCommands, + '_svn': SubversionCommands, + '_darcs': DarcsCommands} + for key in commands: + if key in dirlist: + try: + return commands[key](root) + except (ImportError, OSError): + pass + return FileSystemCommands() + + +class FileSystemCommands(object): + + def create_file(self, path): + open(path, 'w').close() + + def create_folder(self, path): + os.mkdir(path) + + def move(self, path, new_location): + shutil.move(path, new_location) + + def remove(self, path): + if os.path.isfile(path): + os.remove(path) + else: + shutil.rmtree(path) + + def write(self, path, data): + file_ = open(path, 'wb') + try: + file_.write(data) + finally: + file_.close() + + +class SubversionCommands(object): + + def __init__(self, *args): + self.normal_actions = FileSystemCommands() + import pysvn + self.client = pysvn.Client() + + def create_file(self, path): + self.normal_actions.create_file(path) + self.client.add(path, force=True) + + def create_folder(self, path): + self.normal_actions.create_folder(path) + self.client.add(path, force=True) + + def move(self, path, new_location): + self.client.move(path, new_location, force=True) + + def remove(self, path): + self.client.remove(path, force=True) + + def write(self, path, data): + self.normal_actions.write(path, data) + + +class MercurialCommands(object): + + def __init__(self, root): + self.hg = self._import_mercurial() + self.normal_actions = FileSystemCommands() + try: + self.ui = self.hg.ui.ui( + verbose=False, debug=False, quiet=True, + interactive=False, traceback=False, report_untrusted=False) + except: + self.ui = self.hg.ui.ui() + self.ui.setconfig('ui', 'interactive', 'no') + self.ui.setconfig('ui', 'debug', 'no') + self.ui.setconfig('ui', 'traceback', 'no') + self.ui.setconfig('ui', 'verbose', 'no') + self.ui.setconfig('ui', 'report_untrusted', 'no') + self.ui.setconfig('ui', 'quiet', 'yes') + + self.repo = self.hg.hg.repository(self.ui, root) + + def _import_mercurial(self): + import mercurial.commands + import mercurial.hg + import mercurial.ui + return mercurial + + def create_file(self, path): + self.normal_actions.create_file(path) + self.hg.commands.add(self.ui, self.repo, path) + + def create_folder(self, path): + self.normal_actions.create_folder(path) + + def move(self, path, new_location): + self.hg.commands.rename(self.ui, self.repo, path, + new_location, after=False) + + def remove(self, path): + self.hg.commands.remove(self.ui, self.repo, path) + + def write(self, path, data): + self.normal_actions.write(path, data) + + +class GITCommands(object): + + def __init__(self, root): + self.root = root + self._do(['version']) + self.normal_actions = FileSystemCommands() + + def create_file(self, path): + self.normal_actions.create_file(path) + self._do(['add', self._in_dir(path)]) + + def create_folder(self, path): + self.normal_actions.create_folder(path) + + def move(self, path, new_location): + self._do(['mv', self._in_dir(path), self._in_dir(new_location)]) + + def remove(self, path): + self._do(['rm', self._in_dir(path)]) + + def write(self, path, data): + # XXX: should we use ``git add``? + self.normal_actions.write(path, data) + + def _do(self, args): + _execute(['git'] + args, cwd=self.root) + + def _in_dir(self, path): + if path.startswith(self.root): + return path[len(self.root) + 1:] + return self.root + + +class DarcsCommands(object): + + def __init__(self, root): + self.root = root + self.normal_actions = FileSystemCommands() + + def create_file(self, path): + self.normal_actions.create_file(path) + self._do(['add', path]) + + def create_folder(self, path): + self.normal_actions.create_folder(path) + self._do(['add', path]) + + def move(self, path, new_location): + self._do(['mv', path, new_location]) + + def remove(self, path): + self.normal_actions.remove(path) + + def write(self, path, data): + self.normal_actions.write(path, data) + + def _do(self, args): + _execute(['darcs'] + args, cwd=self.root) + + +def _execute(args, cwd=None): + process = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE) + process.wait() + return process.returncode + + +def unicode_to_file_data(contents, encoding=None): + if not isinstance(contents, unicode): + return contents + if encoding is None: + encoding = read_str_coding(contents) + if encoding is not None: + return contents.encode(encoding) + try: + return contents.encode() + except UnicodeEncodeError: + return contents.encode('utf-8') + + +def file_data_to_unicode(data, encoding=None): + result = _decode_data(data, encoding) + if '\r' in result: + result = result.replace('\r\n', '\n').replace('\r', '\n') + return result + + +def _decode_data(data, encoding): + if isinstance(data, unicode): + return data + if encoding is None: + encoding = read_str_coding(data) + if encoding is None: + # there is no encoding tip, we need to guess. + # PEP263 says that "encoding not explicitly defined" means it is ascii, + # but we will use utf8 instead since utf8 fully covers ascii and btw is + # the only non-latin sane encoding. + encoding = 'utf-8' + try: + return data.decode(encoding) + except (UnicodeError, LookupError): + # fallback to latin1: it should never fail + return data.decode('latin1') + + +def read_file_coding(path): + file = open(path, 'b') + count = 0 + result = [] + while True: + current = file.read(10) + if not current: + break + count += current.count('\n') + result.append(current) + file.close() + return _find_coding(''.join(result)) + + +def read_str_coding(source): + if type(source) == bytes: + newline = b'\n' + else: + newline = '\n' + #try: + # source = source.decode("utf-8") + #except AttributeError: + # pass + try: + first = source.index(newline) + 1 + second = source.index(newline, first) + 1 + except ValueError: + second = len(source) + return _find_coding(source[:second]) + + +def _find_coding(text): + if isinstance(text, pycompat.str): + text = text.encode('utf-8') + coding = b'coding' + to_chr = chr if pycompat.PY3 else lambda x: x + try: + start = text.index(coding) + len(coding) + if text[start] not in b'=:': + return + start += 1 + while start < len(text) and to_chr(text[start]).isspace(): + start += 1 + end = start + while end < len(text): + c = text[end] + if not to_chr(c).isalnum() and c not in b'-_': + break + end += 1 + result = text[start:end] + if isinstance(result, bytes): + result = result.decode('utf-8') + return result + except ValueError: + pass diff --git a/venv/Lib/site-packages/rope/base/history.py b/venv/Lib/site-packages/rope/base/history.py new file mode 100644 index 0000000000000000000000000000000000000000..d3c523d310c41b42740253818ce4f94f04e6b988 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/history.py @@ -0,0 +1,235 @@ +from rope.base import exceptions, change, taskhandle + + +class History(object): + """A class that holds project history""" + + def __init__(self, project, maxundos=None): + self.project = project + self._undo_list = [] + self._redo_list = [] + self._maxundos = maxundos + self._load_history() + self.project.data_files.add_write_hook(self.write) + self.current_change = None + + def _load_history(self): + if self.save: + result = self.project.data_files.read_data( + 'history', compress=self.compress, import_=True) + if result is not None: + to_change = change.DataToChange(self.project) + for data in result[0]: + self._undo_list.append(to_change(data)) + for data in result[1]: + self._redo_list.append(to_change(data)) + + def do(self, changes, task_handle=taskhandle.NullTaskHandle()): + """Perform the change and add it to the `self.undo_list` + + Note that uninteresting changes (changes to ignored files) + will not be appended to `self.undo_list`. + + """ + try: + self.current_change = changes + changes.do(change.create_job_set(task_handle, changes)) + finally: + self.current_change = None + if self._is_change_interesting(changes): + self.undo_list.append(changes) + self._remove_extra_items() + del self.redo_list[:] + + def _remove_extra_items(self): + if len(self.undo_list) > self.max_undos: + del self.undo_list[0:len(self.undo_list) - self.max_undos] + + def _is_change_interesting(self, changes): + for resource in changes.get_changed_resources(): + if not self.project.is_ignored(resource): + return True + return False + + def undo(self, change=None, drop=False, + task_handle=taskhandle.NullTaskHandle()): + """Redo done changes from the history + + When `change` is `None`, the last done change will be undone. + If change is not `None` it should be an item from + `self.undo_list`; this change and all changes that depend on + it will be undone. In both cases the list of undone changes + will be returned. + + If `drop` is `True`, the undone change will not be appended to + the redo list. + + """ + if not self._undo_list: + raise exceptions.HistoryError('Undo list is empty') + if change is None: + change = self.undo_list[-1] + dependencies = self._find_dependencies(self.undo_list, change) + self._move_front(self.undo_list, dependencies) + self._perform_undos(len(dependencies), task_handle) + result = self.redo_list[-len(dependencies):] + if drop: + del self.redo_list[-len(dependencies):] + return result + + def redo(self, change=None, task_handle=taskhandle.NullTaskHandle()): + """Redo undone changes from the history + + When `change` is `None`, the last undone change will be + redone. If change is not `None` it should be an item from + `self.redo_list`; this change and all changes that depend on + it will be redone. In both cases the list of redone changes + will be returned. + + """ + if not self.redo_list: + raise exceptions.HistoryError('Redo list is empty') + if change is None: + change = self.redo_list[-1] + dependencies = self._find_dependencies(self.redo_list, change) + self._move_front(self.redo_list, dependencies) + self._perform_redos(len(dependencies), task_handle) + return self.undo_list[-len(dependencies):] + + def _move_front(self, change_list, changes): + for change in changes: + change_list.remove(change) + change_list.append(change) + + def _find_dependencies(self, change_list, change): + index = change_list.index(change) + return _FindChangeDependencies(change_list[index:])() + + def _perform_undos(self, count, task_handle): + for i in range(count): + self.current_change = self.undo_list[-1] + try: + job_set = change.create_job_set(task_handle, + self.current_change) + self.current_change.undo(job_set) + finally: + self.current_change = None + self.redo_list.append(self.undo_list.pop()) + + def _perform_redos(self, count, task_handle): + for i in range(count): + self.current_change = self.redo_list[-1] + try: + job_set = change.create_job_set(task_handle, + self.current_change) + self.current_change.do(job_set) + finally: + self.current_change = None + self.undo_list.append(self.redo_list.pop()) + + def contents_before_current_change(self, file): + if self.current_change is None: + return None + result = self._search_for_change_contents([self.current_change], file) + if result is not None: + return result + if file.exists() and not file.is_folder(): + return file.read() + else: + return None + + def _search_for_change_contents(self, change_list, file): + for change_ in reversed(change_list): + if isinstance(change_, change.ChangeSet): + result = self._search_for_change_contents(change_.changes, + file) + if result is not None: + return result + if isinstance(change_, change.ChangeContents) and \ + change_.resource == file: + return change_.old_contents + + def write(self): + if self.save: + data = [] + to_data = change.ChangeToData() + self._remove_extra_items() + data.append([to_data(change_) for change_ in self.undo_list]) + data.append([to_data(change_) for change_ in self.redo_list]) + self.project.data_files.write_data('history', data, + compress=self.compress) + + def get_file_undo_list(self, resource): + result = [] + for change in self.undo_list: + if resource in change.get_changed_resources(): + result.append(change) + return result + + def __str__(self): + return 'History holds %s changes in memory' % \ + (len(self.undo_list) + len(self.redo_list)) + + undo_list = property(lambda self: self._undo_list) + redo_list = property(lambda self: self._redo_list) + + @property + def tobe_undone(self): + """The last done change if available, `None` otherwise""" + if self.undo_list: + return self.undo_list[-1] + + @property + def tobe_redone(self): + """The last undone change if available, `None` otherwise""" + if self.redo_list: + return self.redo_list[-1] + + @property + def max_undos(self): + if self._maxundos is None: + return self.project.prefs.get('max_history_items', 100) + else: + return self._maxundos + + @property + def save(self): + return self.project.prefs.get('save_history', False) + + @property + def compress(self): + return self.project.prefs.get('compress_history', False) + + def clear(self): + """Forget all undo and redo information""" + del self.undo_list[:] + del self.redo_list[:] + + +class _FindChangeDependencies(object): + + def __init__(self, change_list): + self.change = change_list[0] + self.change_list = change_list + self.changed_resources = set(self.change.get_changed_resources()) + + def __call__(self): + result = [self.change] + for change in self.change_list[1:]: + if self._depends_on(change, result): + result.append(change) + self.changed_resources.update(change.get_changed_resources()) + return result + + def _depends_on(self, changes, result): + for resource in changes.get_changed_resources(): + if resource is None: + continue + if resource in self.changed_resources: + return True + for changed in self.changed_resources: + if resource.is_folder() and resource.contains(changed): + return True + if changed.is_folder() and changed.contains(resource): + return True + return False diff --git a/venv/Lib/site-packages/rope/base/libutils.py b/venv/Lib/site-packages/rope/base/libutils.py new file mode 100644 index 0000000000000000000000000000000000000000..4037f183f576c23876daf62eba8c9a4644c625f2 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/libutils.py @@ -0,0 +1,122 @@ +"""A few useful functions for using rope as a library""" +import os.path + +import rope.base.project +import rope.base.pycore +from rope.base import pyobjectsdef +from rope.base import utils +from rope.base import taskhandle + + +def path_to_resource(project, path, type=None): + """Get the resource at path + + You only need to specify `type` if `path` does not exist. It can + be either 'file' or 'folder'. If the type is `None` it is assumed + that the resource already exists. + + Note that this function uses `Project.get_resource()`, + `Project.get_file()`, and `Project.get_folder()` methods. + + """ + project_path = path_relative_to_project_root(project, path) + if project_path is None: + project_path = rope.base.project._realpath(path) + project = rope.base.project.get_no_project() + if type is None: + return project.get_resource(project_path) + if type == 'file': + return project.get_file(project_path) + if type == 'folder': + return project.get_folder(project_path) + return None + + +def path_relative_to_project_root(project, path): + return relative(project.address, path) + +@utils.deprecated() +def relative(root, path): + root = rope.base.project._realpath(root).replace(os.path.sep, '/') + path = rope.base.project._realpath(path).replace(os.path.sep, '/') + if path == root: + return '' + if path.startswith(root + '/'): + return path[len(root) + 1:] + + +def report_change(project, path, old_content): + """Report that the contents of file at `path` was changed + + The new contents of file is retrieved by reading the file. + + """ + resource = path_to_resource(project, path) + if resource is None: + return + for observer in list(project.observers): + observer.resource_changed(resource) + if project.pycore.automatic_soa: + rope.base.pycore.perform_soa_on_changed_scopes(project, resource, + old_content) + + +def analyze_module(project, resource): + """Perform static object analysis on a python file in the project + + Note that this might be really time consuming. + """ + project.pycore.analyze_module(resource) + + +def analyze_modules(project, task_handle=taskhandle.NullTaskHandle()): + """Perform static object analysis on all python files in the project + + Note that this might be really time consuming. + """ + resources = project.get_python_files() + job_set = task_handle.create_jobset('Analyzing Modules', len(resources)) + for resource in resources: + job_set.started_job(resource.path) + analyze_module(project, resource) + job_set.finished_job() + + +def get_string_module(project, code, resource=None, force_errors=False): + """Returns a `PyObject` object for the given code + + If `force_errors` is `True`, `exceptions.ModuleSyntaxError` is + raised if module has syntax errors. This overrides + ``ignore_syntax_errors`` project config. + + """ + return pyobjectsdef.PyModule(project.pycore, code, resource, + force_errors=force_errors) + + +def get_string_scope(project, code, resource=None): + """Returns a `Scope` object for the given code""" + return get_string_module(project, code, resource).get_scope() + + +def is_python_file(project, resource): + return project.pycore.is_python_file(resource) + + +def modname(resource): + if resource.is_folder(): + module_name = resource.name + source_folder = resource.parent + elif resource.name == '__init__.py': + module_name = resource.parent.name + source_folder = resource.parent.parent + else: + module_name = resource.name[:-3] + source_folder = resource.parent + + while source_folder != source_folder.parent and \ + source_folder.has_child('__init__.py'): + module_name = source_folder.name + '.' + module_name + source_folder = source_folder.parent + + return module_name diff --git a/venv/Lib/site-packages/rope/base/oi/__init__.py b/venv/Lib/site-packages/rope/base/oi/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0b1a1525c088b696e674fcb7b623ff44456cda92 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/__init__.py @@ -0,0 +1,38 @@ +"""Rope object analysis and inference package + +Rope makes some simplifying assumptions about a python program. It +assumes that a program only performs assignments and function calls. +Tracking assignments is simple and `PyName` objects handle that. The +main problem is function calls. Rope uses these two approaches for +obtaining call information: + +* Static object analysis: `rope.base.pycore.PyCore.analyze_module()` + + It can analyze modules to obtain information about functions. This + is done by analyzing function calls in a module or scope. Currently + SOA analyzes the scopes that are changed while saving or when the + user asks to analyze a module. That is mainly because static + analysis is time-consuming. + +* Dynamic object analysis: `rope.base.pycore.PyCore.run_module()` + + When you run a module or your testsuite, when DOA is enabled, it + collects information about parameters passed to and objects returned + from functions. The main problem with this approach is that it is + quite slow; Not when looking up the information but when collecting + them. + +An instance of `rope.base.oi.objectinfo.ObjectInfoManager` can be used +for accessing these information. It saves the data in a +`rope.base.oi.objectdb.ObjectDB` internally. + +Now if our objectdb does not know anything about a function and we +need the value returned by it, static object inference, SOI, comes +into play. It analyzes function body and tries to infer the object +that is returned from it (we usually need the returned value for the +given parameter objects). + +Rope might collect and store information for other `PyName`\s, too. +For instance rope stores the object builtin containers hold. + +""" diff --git a/venv/Lib/site-packages/rope/base/oi/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f62221bfb6b424667362885df73926300237aa10 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/__pycache__/__init__.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/__pycache__/doa.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/__pycache__/doa.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3fe5c7e1d324803c8b72ca240fc4aacd4f9c2400 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/__pycache__/doa.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/__pycache__/memorydb.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/__pycache__/memorydb.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2073b12a58e9dff30576161fcad676d4d3ac2502 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/__pycache__/memorydb.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/__pycache__/objectdb.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/__pycache__/objectdb.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d8a7c499b2bca10b97242a8463002fe2bfda89af Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/__pycache__/objectdb.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/__pycache__/objectinfo.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/__pycache__/objectinfo.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b943658349e5946fce325d3463be69dcef32cfb Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/__pycache__/objectinfo.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/__pycache__/runmod.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/__pycache__/runmod.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..585e8ead65877d59648dc24b7d51b941639b9696 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/__pycache__/runmod.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/__pycache__/soa.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/__pycache__/soa.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ddadf0462b2beb9c42f4bc9a53d6128a73e72d4a Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/__pycache__/soa.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/__pycache__/soi.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/__pycache__/soi.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5043e5157f22b71651b61d80c975ec4ff3c7f420 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/__pycache__/soi.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/__pycache__/transform.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/__pycache__/transform.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..64d7b022935a2fb4bdcc6b90a6ab9f6ba714abb7 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/__pycache__/transform.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/doa.py b/venv/Lib/site-packages/rope/base/oi/doa.py new file mode 100644 index 0000000000000000000000000000000000000000..63ebc50ea762b6469996fa38dc5f0893650a966e --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/doa.py @@ -0,0 +1,222 @@ +import base64 +import hashlib +import hmac +try: + import cPickle as pickle +except ImportError: + import pickle +import marshal +import os +import socket +import subprocess +import sys +import tempfile +import threading + + +def _compat_compare_digest(a, b): + """Implementation of hmac.compare_digest for python < 2.7.7. + + This function uses an approach designed to prevent timing analysis by + avoiding content-based short circuiting behaviour, making it appropriate + for cryptography. + """ + if len(a) != len(b): + return False + # Computes the bitwise difference of all characters in the two strings + # before returning whether or not they are equal. + difference = 0 + for (a_char, b_char) in zip(a, b): + difference |= ord(a_char) ^ ord(b_char) + return difference == 0 + +try: + from hmac import compare_digest +except ImportError: + compare_digest = _compat_compare_digest + + +class PythonFileRunner(object): + """A class for running python project files""" + + def __init__(self, pycore, file_, args=None, stdin=None, + stdout=None, analyze_data=None): + self.pycore = pycore + self.file = file_ + self.analyze_data = analyze_data + self.observers = [] + self.args = args + self.stdin = stdin + self.stdout = stdout + + def run(self): + """Execute the process""" + env = dict(os.environ) + file_path = self.file.real_path + path_folders = self.pycore.project.get_source_folders() + \ + self.pycore.project.get_python_path_folders() + env['PYTHONPATH'] = os.pathsep.join(folder.real_path + for folder in path_folders) + runmod_path = self.pycore.project.find_module('rope.base.oi.runmod').real_path + self.receiver = None + self._init_data_receiving() + send_info = '-' + if self.receiver: + send_info = self.receiver.get_send_info() + args = [sys.executable, runmod_path, send_info, + self.pycore.project.address, self.file.real_path] + if self.analyze_data is None: + del args[1:4] + if self.args is not None: + args.extend(self.args) + self.process = subprocess.Popen( + executable=sys.executable, args=args, env=env, + cwd=os.path.split(file_path)[0], stdin=self.stdin, + stdout=self.stdout, stderr=self.stdout, close_fds=os.name != 'nt') + + def _init_data_receiving(self): + if self.analyze_data is None: + return + # Disabling FIFO data transfer due to blocking when running + # unittests in the GUI. + # XXX: Handle FIFO data transfer for `rope.ui.testview` + if True or os.name == 'nt': + self.receiver = _SocketReceiver() + else: + self.receiver = _FIFOReceiver() + self.receiving_thread = threading.Thread( + target=self._receive_information) + self.receiving_thread.setDaemon(True) + self.receiving_thread.start() + + def _receive_information(self): + #temp = open('/dev/shm/info', 'wb') + for data in self.receiver.receive_data(): + self.analyze_data(data) + #temp.write(str(data) + '\n') + #temp.close() + for observer in self.observers: + observer() + + def wait_process(self): + """Wait for the process to finish""" + self.process.wait() + if self.analyze_data: + self.receiving_thread.join() + + def kill_process(self): + """Stop the process""" + if self.process.poll() is not None: + return + try: + if hasattr(self.process, 'terminate'): + self.process.terminate() + elif os.name != 'nt': + os.kill(self.process.pid, 9) + else: + import ctypes + handle = int(self.process._handle) + ctypes.windll.kernel32.TerminateProcess(handle, -1) + except OSError: + pass + + def add_finishing_observer(self, observer): + """Notify this observer when execution finishes""" + self.observers.append(observer) + + +class _MessageReceiver(object): + + def receive_data(self): + pass + + def get_send_info(self): + pass + + +class _SocketReceiver(_MessageReceiver): + + def __init__(self): + self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.data_port = 3037 + self.key = os.urandom(32) + + while self.data_port < 4000: + try: + self.server_socket.bind(('localhost', self.data_port)) + break + except socket.error: + self.data_port += 1 + self.server_socket.listen(1) + + def get_send_info(self): + return '%d:%s' % (self.data_port, + base64.b64encode(self.key).decode('utf-8')) + + def receive_data(self): + conn, addr = self.server_socket.accept() + self.server_socket.close() + my_file = conn.makefile('rb') + while True: + # Received messages must meet the following criteria: + # 1. Must be contained on a single line. + # 2. Must be prefixed with a base64 encoded sha256 message digest + # of the base64 encoded pickle data. + # 3. Message digest must be computed using the correct key. + # + # Any messages received that do not meet these criteria will never + # be unpickled and will be dropped silently. + try: + buf = my_file.readline() + if len(buf) == 0: + break + + try: + digest_end = buf.index(b':') + buf_digest = base64.b64decode(buf[:digest_end]) + buf_data = buf[digest_end + 1:-1] + decoded_buf_data = base64.b64decode(buf_data) + except: + # Corrupted data; the payload cannot be trusted and just has + # to be dropped. See CVE-2014-3539. + continue + + digest = hmac.new(self.key, buf_data, hashlib.sha256).digest() + if not compare_digest(buf_digest, digest): + # Signature mismatch; the payload cannot be trusted and just + # has to be dropped. See CVE-2014-3539. + continue + + yield pickle.loads(decoded_buf_data) + except EOFError: + break + my_file.close() + conn.close() + + +class _FIFOReceiver(_MessageReceiver): + + def __init__(self): + # XXX: this is insecure and might cause race conditions + self.file_name = self._get_file_name() + os.mkfifo(self.file_name) + + def _get_file_name(self): + prefix = tempfile.gettempdir() + '/__rope_' + i = 0 + while os.path.exists(prefix + str(i).rjust(4, '0')): + i += 1 + return prefix + str(i).rjust(4, '0') + + def get_send_info(self): + return self.file_name + + def receive_data(self): + my_file = open(self.file_name, 'rb') + while True: + try: + yield marshal.load(my_file) + except EOFError: + break + my_file.close() + os.remove(self.file_name) diff --git a/venv/Lib/site-packages/rope/base/oi/memorydb.py b/venv/Lib/site-packages/rope/base/oi/memorydb.py new file mode 100644 index 0000000000000000000000000000000000000000..01c814ce460afc8463a9e5aa26e9ddaefd5f3755 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/memorydb.py @@ -0,0 +1,127 @@ +from rope.base.oi import objectdb + + +class MemoryDB(objectdb.FileDict): + + def __init__(self, project, persist=None): + self.project = project + self._persist = persist + self.files = self + self._load_files() + self.project.data_files.add_write_hook(self.write) + + def _load_files(self): + self._files = {} + if self.persist: + result = self.project.data_files.read_data( + 'objectdb', compress=self.compress, import_=True) + if result is not None: + self._files = result + + def keys(self): + return self._files.keys() + + def __iter__(self): + for f in self._files: + yield f + + def __len__(self): + return len(self._files) + + def __setitem__(self): + raise NotImplementedError() + + def __contains__(self, key): + return key in self._files + + def __getitem__(self, key): + return FileInfo(self._files[key]) + + def create(self, path): + self._files[path] = {} + + def rename(self, file, newfile): + if file not in self._files: + return + self._files[newfile] = self._files[file] + del self[file] + + def __delitem__(self, file): + del self._files[file] + + def write(self): + if self.persist: + self.project.data_files.write_data('objectdb', self._files, + self.compress) + + @property + def compress(self): + return self.project.prefs.get('compress_objectdb', False) + + @property + def persist(self): + if self._persist is not None: + return self._persist + else: + return self.project.prefs.get('save_objectdb', False) + + +class FileInfo(objectdb.FileInfo): + + def __init__(self, scopes): + self.scopes = scopes + + def create_scope(self, key): + self.scopes[key] = ScopeInfo() + + def keys(self): + return self.scopes.keys() + + def __contains__(self, key): + return key in self.scopes + + def __getitem__(self, key): + return self.scopes[key] + + def __delitem__(self, key): + del self.scopes[key] + + def __iter__(self): + for s in self.scopes: + yield s + + def __len__(self): + return len(self.scopes) + + def __setitem__(self): + raise NotImplementedError() + + + +class ScopeInfo(objectdb.ScopeInfo): + + def __init__(self): + self.call_info = {} + self.per_name = {} + + def get_per_name(self, name): + return self.per_name.get(name, None) + + def save_per_name(self, name, value): + self.per_name[name] = value + + def get_returned(self, parameters): + return self.call_info.get(parameters, None) + + def get_call_infos(self): + for args, returned in self.call_info.items(): + yield objectdb.CallInfo(args, returned) + + def add_call(self, parameters, returned): + self.call_info[parameters] = returned + + def __getstate__(self): + return (self.call_info, self.per_name) + + def __setstate__(self, data): + self.call_info, self.per_name = data diff --git a/venv/Lib/site-packages/rope/base/oi/objectdb.py b/venv/Lib/site-packages/rope/base/oi/objectdb.py new file mode 100644 index 0000000000000000000000000000000000000000..61f2711dd4e0a3d559798ed5e0471f56b54faff5 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/objectdb.py @@ -0,0 +1,179 @@ +from __future__ import print_function +try: + from collections import MutableMapping +except ImportError: + from UserDict import DictMixin as MutableMapping + + +class ObjectDB(object): + + def __init__(self, db, validation): + self.db = db + self.validation = validation + self.observers = [] + self.files = db.files + + def validate_files(self): + for file in list(self.files): + if not self.validation.is_file_valid(file): + del self.files[file] + self._file_removed(file) + + def validate_file(self, file): + if file not in self.files: + return + for key in list(self.files[file]): + if not self.validation.is_scope_valid(file, key): + del self.files[file][key] + + def file_moved(self, file, newfile): + if file not in self.files: + return + self.files.rename(file, newfile) + self._file_removed(file) + self._file_added(newfile) + + def get_files(self): + return self.files.keys() + + def get_returned(self, path, key, args): + scope_info = self._get_scope_info(path, key, readonly=True) + result = scope_info.get_returned(args) + if self.validation.is_value_valid(result): + return result + + def get_pername(self, path, key, name): + scope_info = self._get_scope_info(path, key, readonly=True) + result = scope_info.get_per_name(name) + if self.validation.is_value_valid(result): + return result + + def get_callinfos(self, path, key): + scope_info = self._get_scope_info(path, key, readonly=True) + return scope_info.get_call_infos() + + def add_callinfo(self, path, key, args, returned): + scope_info = self._get_scope_info(path, key, readonly=False) + old_returned = scope_info.get_returned(args) + if self.validation.is_more_valid(returned, old_returned): + scope_info.add_call(args, returned) + + def add_pername(self, path, key, name, value): + scope_info = self._get_scope_info(path, key, readonly=False) + old_value = scope_info.get_per_name(name) + if self.validation.is_more_valid(value, old_value): + scope_info.save_per_name(name, value) + + def add_file_list_observer(self, observer): + self.observers.append(observer) + + def write(self): + self.db.write() + + def _get_scope_info(self, path, key, readonly=True): + if path not in self.files: + if readonly: + return _NullScopeInfo() + self.files.create(path) + self._file_added(path) + if key not in self.files[path]: + if readonly: + return _NullScopeInfo() + self.files[path].create_scope(key) + result = self.files[path][key] + if isinstance(result, dict): + print(self.files, self.files[path], self.files[path][key]) + return result + + def _file_removed(self, path): + for observer in self.observers: + observer.removed(path) + + def _file_added(self, path): + for observer in self.observers: + observer.added(path) + + def __str__(self): + scope_count = 0 + for file_dict in self.files.values(): + scope_count += len(file_dict) + return 'ObjectDB holds %s file and %s scope infos' % \ + (len(self.files), scope_count) + + +class _NullScopeInfo(object): + + def __init__(self, error_on_write=True): + self.error_on_write = error_on_write + + def get_per_name(self, name): + pass + + def save_per_name(self, name, value): + if self.error_on_write: + raise NotImplementedError() + + def get_returned(self, parameters): + pass + + def get_call_infos(self): + return [] + + def add_call(self, parameters, returned): + if self.error_on_write: + raise NotImplementedError() + + +class FileInfo(MutableMapping): + + def create_scope(self, key): + pass + + +class FileDict(MutableMapping): + + def create(self, key): + pass + + def rename(self, key, new_key): + pass + + +class ScopeInfo(object): + + def get_per_name(self, name): + pass + + def save_per_name(self, name, value): + pass + + def get_returned(self, parameters): + pass + + def get_call_infos(self): + pass + + def add_call(self, parameters, returned): + pass + + +class CallInfo(object): + + def __init__(self, args, returned): + self.args = args + self.returned = returned + + def get_parameters(self): + return self.args + + def get_returned(self): + return self.returned + + +class FileListObserver(object): + + def added(self, path): + pass + + def removed(self, path): + pass diff --git a/venv/Lib/site-packages/rope/base/oi/objectinfo.py b/venv/Lib/site-packages/rope/base/oi/objectinfo.py new file mode 100644 index 0000000000000000000000000000000000000000..f86d72e0b5cc26f2432aa678f7e5144451f5e8fa --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/objectinfo.py @@ -0,0 +1,232 @@ +import warnings + +from rope.base import exceptions, resourceobserver +from rope.base.oi import objectdb, memorydb, transform + + +class ObjectInfoManager(object): + """Stores object information + + It uses an instance of `objectdb.ObjectDB` for storing + information. + + """ + + def __init__(self, project): + self.project = project + self.to_textual = transform.PyObjectToTextual(project) + self.to_pyobject = transform.TextualToPyObject(project) + self.doi_to_pyobject = transform.DOITextualToPyObject(project) + self._init_objectdb() + if project.prefs.get('validate_objectdb', False): + self._init_validation() + + def _init_objectdb(self): + dbtype = self.project.get_prefs().get('objectdb_type', None) + persist = None + if dbtype is not None: + warnings.warn( + '"objectdb_type" project config is deprecated;\n' + 'Use "save_objectdb" instead in your project ' + 'config file.\n(".ropeproject/config.py" by default)\n', + DeprecationWarning) + if dbtype != 'memory' and self.project.ropefolder is not None: + persist = True + self.validation = TextualValidation(self.to_pyobject) + db = memorydb.MemoryDB(self.project, persist=persist) + self.objectdb = objectdb.ObjectDB(db, self.validation) + + def _init_validation(self): + self.objectdb.validate_files() + observer = resourceobserver.ResourceObserver( + changed=self._resource_changed, moved=self._resource_moved, + removed=self._resource_moved) + files = [] + for path in self.objectdb.get_files(): + resource = self.to_pyobject.path_to_resource(path) + if resource is not None and resource.project == self.project: + files.append(resource) + self.observer = resourceobserver.FilteredResourceObserver(observer, + files) + self.objectdb.add_file_list_observer(_FileListObserver(self)) + self.project.add_observer(self.observer) + + def _resource_changed(self, resource): + try: + self.objectdb.validate_file( + self.to_textual.resource_to_path(resource)) + except exceptions.ModuleSyntaxError: + pass + + def _resource_moved(self, resource, new_resource=None): + self.observer.remove_resource(resource) + if new_resource is not None: + old = self.to_textual.resource_to_path(resource) + new = self.to_textual.resource_to_path(new_resource) + self.objectdb.file_moved(old, new) + self.observer.add_resource(new_resource) + + def get_returned(self, pyobject, args): + result = self.get_exact_returned(pyobject, args) + if result is not None: + return result + path, key = self._get_scope(pyobject) + if path is None: + return None + for call_info in self.objectdb.get_callinfos(path, key): + returned = call_info.get_returned() + if returned and returned[0] not in ('unknown', 'none'): + result = returned + break + if result is None: + result = returned + if result is not None: + return self.to_pyobject(result) + + def get_exact_returned(self, pyobject, args): + path, key = self._get_scope(pyobject) + if path is not None: + returned = self.objectdb.get_returned( + path, key, self._args_to_textual(pyobject, args)) + if returned is not None: + return self.to_pyobject(returned) + + def _args_to_textual(self, pyfunction, args): + parameters = list(pyfunction.get_param_names(special_args=False)) + arguments = args.get_arguments(parameters)[:len(parameters)] + textual_args = tuple([self.to_textual(arg) + for arg in arguments]) + return textual_args + + def get_parameter_objects(self, pyobject): + path, key = self._get_scope(pyobject) + if path is None: + return None + arg_count = len(pyobject.get_param_names(special_args=False)) + unknowns = arg_count + parameters = [None] * arg_count + for call_info in self.objectdb.get_callinfos(path, key): + args = call_info.get_parameters() + for index, arg in enumerate(args[:arg_count]): + old = parameters[index] + if self.validation.is_more_valid(arg, old): + parameters[index] = arg + if self.validation.is_value_valid(arg): + unknowns -= 1 + if unknowns == 0: + break + if unknowns < arg_count: + return [self.to_pyobject(parameter) + for parameter in parameters] + + def get_passed_objects(self, pyfunction, parameter_index): + path, key = self._get_scope(pyfunction) + if path is None: + return [] + result = [] + for call_info in self.objectdb.get_callinfos(path, key): + args = call_info.get_parameters() + if len(args) > parameter_index: + parameter = self.to_pyobject(args[parameter_index]) + if parameter is not None: + result.append(parameter) + return result + + def doa_data_received(self, data): + def doi_to_normal(textual): + pyobject = self.doi_to_pyobject(textual) + return self.to_textual(pyobject) + function = doi_to_normal(data[0]) + args = tuple([doi_to_normal(textual) for textual in data[1]]) + returned = doi_to_normal(data[2]) + if function[0] == 'defined' and len(function) == 3: + self._save_data(function, args, returned) + + def function_called(self, pyfunction, params, returned=None): + function_text = self.to_textual(pyfunction) + params_text = tuple([self.to_textual(param) + for param in params]) + returned_text = ('unknown',) + if returned is not None: + returned_text = self.to_textual(returned) + self._save_data(function_text, params_text, returned_text) + + def save_per_name(self, scope, name, data): + path, key = self._get_scope(scope.pyobject) + if path is not None: + self.objectdb.add_pername(path, key, name, self.to_textual(data)) + + def get_per_name(self, scope, name): + path, key = self._get_scope(scope.pyobject) + if path is not None: + result = self.objectdb.get_pername(path, key, name) + if result is not None: + return self.to_pyobject(result) + + def _save_data(self, function, args, returned=('unknown',)): + self.objectdb.add_callinfo(function[1], function[2], args, returned) + + def _get_scope(self, pyobject): + resource = pyobject.get_module().get_resource() + if resource is None: + return None, None + textual = self.to_textual(pyobject) + if textual[0] == 'defined': + path = textual[1] + if len(textual) == 3: + key = textual[2] + else: + key = '' + return path, key + return None, None + + def sync(self): + self.objectdb.sync() + + def __str__(self): + return str(self.objectdb) + + +class TextualValidation(object): + + def __init__(self, to_pyobject): + self.to_pyobject = to_pyobject + + def is_value_valid(self, value): + # ???: Should none and unknown be considered valid? + if value is None or value[0] in ('none', 'unknown'): + return False + return self.to_pyobject(value) is not None + + def is_more_valid(self, new, old): + if old is None: + return True + return new[0] not in ('unknown', 'none') + + def is_file_valid(self, path): + return self.to_pyobject.path_to_resource(path) is not None + + def is_scope_valid(self, path, key): + if key == '': + textual = ('defined', path) + else: + textual = ('defined', path, key) + return self.to_pyobject(textual) is not None + + +class _FileListObserver(object): + + def __init__(self, object_info): + self.object_info = object_info + self.observer = self.object_info.observer + self.to_pyobject = self.object_info.to_pyobject + + def removed(self, path): + resource = self.to_pyobject.path_to_resource(path) + if resource is not None: + self.observer.remove_resource(resource) + + def added(self, path): + resource = self.to_pyobject.path_to_resource(path) + if resource is not None: + self.observer.add_resource(resource) diff --git a/venv/Lib/site-packages/rope/base/oi/runmod.py b/venv/Lib/site-packages/rope/base/oi/runmod.py new file mode 100644 index 0000000000000000000000000000000000000000..055d9ae86021b0c96189d3f5e5cbbe35b21f82b8 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/runmod.py @@ -0,0 +1,230 @@ +def __rope_start_everything(): + import os + import sys + import socket + try: + import cPickle as pickle + except ImportError: + import pickle + import marshal + import inspect + import types + import threading + import rope.base.utils.pycompat as pycompat + import base64 + import hashlib + import hmac + + class _MessageSender(object): + + def send_data(self, data): + pass + + class _SocketSender(_MessageSender): + + def __init__(self, port, key): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect(('127.0.0.1', port)) + self.my_file = s.makefile('wb') + self.key = base64.b64decode(key) + + def send_data(self, data): + if not self.my_file.closed: + pickled_data = base64.b64encode( + pickle.dumps(data, pickle.HIGHEST_PROTOCOL)) + dgst = hmac.new(self.key, pickled_data, hashlib.sha256).digest() + self.my_file.write(base64.b64encode(dgst) + b':' + + pickled_data + b'\n') + def close(self): + self.my_file.close() + + class _FileSender(_MessageSender): + + def __init__(self, file_name): + self.my_file = open(file_name, 'wb') + + def send_data(self, data): + if not self.my_file.closed: + marshal.dump(data, self.my_file) + + def close(self): + self.my_file.close() + + def _cached(func): + cache = {} + + def newfunc(self, arg): + if arg in cache: + return cache[arg] + result = func(self, arg) + cache[arg] = result + return result + return newfunc + + class _FunctionCallDataSender(object): + + def __init__(self, send_info, project_root): + self.project_root = project_root + if send_info[0].isdigit(): + port, key = send_info.split(':', 1) + self.sender = _SocketSender(int(port), key) + else: + self.sender = _FileSender(send_info) + + def global_trace(frame, event, arg): + # HACK: Ignoring out->in calls + # This might lose some information + if self._is_an_interesting_call(frame): + return self.on_function_call + sys.settrace(global_trace) + threading.settrace(global_trace) + + def on_function_call(self, frame, event, arg): + if event != 'return': + return + args = [] + returned = ('unknown',) + code = frame.f_code + for argname in code.co_varnames[:code.co_argcount]: + try: + argvalue = self._object_to_persisted_form( + frame.f_locals[argname]) + args.append(argvalue) + except (TypeError, AttributeError): + args.append(('unknown',)) + try: + returned = self._object_to_persisted_form(arg) + except (TypeError, AttributeError): + pass + try: + data = (self._object_to_persisted_form(frame.f_code), + tuple(args), returned) + self.sender.send_data(data) + except (TypeError): + pass + return self.on_function_call + + def _is_an_interesting_call(self, frame): + #if frame.f_code.co_name in ['?', '<module>']: + # return False + #return not frame.f_back or + # not self._is_code_inside_project(frame.f_back.f_code) + if not self._is_code_inside_project(frame.f_code) and \ + (not frame.f_back or + not self._is_code_inside_project(frame.f_back.f_code)): + return False + return True + + def _is_code_inside_project(self, code): + source = self._path(code.co_filename) + return source is not None and os.path.exists(source) and \ + _realpath(source).startswith(self.project_root) + + @_cached + def _get_persisted_code(self, object_): + source = self._path(object_.co_filename) + if not os.path.exists(source): + raise TypeError('no source') + return ('defined', _realpath(source), str(object_.co_firstlineno)) + + @_cached + def _get_persisted_class(self, object_): + try: + return ('defined', _realpath(inspect.getsourcefile(object_)), + object_.__name__) + except (TypeError, AttributeError): + return ('unknown',) + + def _get_persisted_builtin(self, object_): + if isinstance(object_, pycompat.string_types): + return ('builtin', 'str') + if isinstance(object_, list): + holding = None + if len(object_) > 0: + holding = object_[0] + return ('builtin', 'list', + self._object_to_persisted_form(holding)) + if isinstance(object_, dict): + keys = None + values = None + if len(object_) > 0: + # @todo - fix it properly, why is __locals__ being + # duplicated ? + keys = [key for key in object_.keys() if key != '__locals__'][0] + values = object_[keys] + return ('builtin', 'dict', + self._object_to_persisted_form(keys), + self._object_to_persisted_form(values)) + if isinstance(object_, tuple): + objects = [] + if len(object_) < 3: + for holding in object_: + objects.append(self._object_to_persisted_form(holding)) + else: + objects.append(self._object_to_persisted_form(object_[0])) + return tuple(['builtin', 'tuple'] + objects) + if isinstance(object_, set): + holding = None + if len(object_) > 0: + for o in object_: + holding = o + break + return ('builtin', 'set', + self._object_to_persisted_form(holding)) + return ('unknown',) + + def _object_to_persisted_form(self, object_): + if object_ is None: + return ('none',) + if isinstance(object_, types.CodeType): + return self._get_persisted_code(object_) + if isinstance(object_, types.FunctionType): + return self._get_persisted_code(object_.__code__) + if isinstance(object_, types.MethodType): + return self._get_persisted_code(object_.__func__.__code__) + if isinstance(object_, types.ModuleType): + return self._get_persisted_module(object_) + if isinstance(object_, pycompat.string_types + (list, dict, tuple, set)): + return self._get_persisted_builtin(object_) + if isinstance(object_, type): + return self._get_persisted_class(object_) + return ('instance', self._get_persisted_class(type(object_))) + + @_cached + def _get_persisted_module(self, object_): + path = self._path(object_.__file__) + if path and os.path.exists(path): + return ('defined', _realpath(path)) + return ('unknown',) + + def _path(self, path): + if path.endswith('.pyc'): + path = path[:-1] + if path.endswith('.py'): + return path + + def close(self): + self.sender.close() + sys.settrace(None) + + def _realpath(path): + return os.path.realpath(os.path.abspath(os.path.expanduser(path))) + + send_info = sys.argv[1] + project_root = sys.argv[2] + file_to_run = sys.argv[3] + run_globals = globals() + run_globals.update({'__name__': '__main__', + '__builtins__': __builtins__, + '__file__': file_to_run}) + + if send_info != '-': + data_sender = _FunctionCallDataSender(send_info, project_root) + del sys.argv[1:4] + pycompat.execfile(file_to_run, run_globals) + if send_info != '-': + data_sender.close() + + +if __name__ == '__main__': + __rope_start_everything() diff --git a/venv/Lib/site-packages/rope/base/oi/soa.py b/venv/Lib/site-packages/rope/base/oi/soa.py new file mode 100644 index 0000000000000000000000000000000000000000..a34b970ea2f599189ad959f7a97ad6e4a6ec8275 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/soa.py @@ -0,0 +1,139 @@ +import rope.base.ast +import rope.base.oi.soi +import rope.base.pynames +from rope.base import pyobjects, evaluate, astutils, arguments + + +def analyze_module(pycore, pymodule, should_analyze, + search_subscopes, followed_calls): + """Analyze `pymodule` for static object inference + + Analyzes scopes for collecting object information. The analysis + starts from inner scopes. + + """ + _analyze_node(pycore, pymodule, should_analyze, + search_subscopes, followed_calls) + + +def _analyze_node(pycore, pydefined, should_analyze, + search_subscopes, followed_calls): + if search_subscopes(pydefined): + for scope in pydefined.get_scope().get_scopes(): + _analyze_node(pycore, scope.pyobject, should_analyze, + search_subscopes, followed_calls) + if should_analyze(pydefined): + new_followed_calls = max(0, followed_calls - 1) + return_true = lambda pydefined: True + return_false = lambda pydefined: False + + def _follow(pyfunction): + _analyze_node(pycore, pyfunction, return_true, + return_false, new_followed_calls) + + if not followed_calls: + _follow = None + visitor = SOAVisitor(pycore, pydefined, _follow) + for child in rope.base.ast.get_child_nodes(pydefined.get_ast()): + rope.base.ast.walk(child, visitor) + + +class SOAVisitor(object): + + def __init__(self, pycore, pydefined, follow_callback=None): + self.pycore = pycore + self.pymodule = pydefined.get_module() + self.scope = pydefined.get_scope() + self.follow = follow_callback + + def _FunctionDef(self, node): + pass + + def _ClassDef(self, node): + pass + + def _Call(self, node): + for child in rope.base.ast.get_child_nodes(node): + rope.base.ast.walk(child, self) + primary, pyname = evaluate.eval_node2(self.scope, node.func) + if pyname is None: + return + pyfunction = pyname.get_object() + if isinstance(pyfunction, pyobjects.AbstractFunction): + args = arguments.create_arguments(primary, pyfunction, + node, self.scope) + elif isinstance(pyfunction, pyobjects.PyClass): + pyclass = pyfunction + if '__init__' in pyfunction: + pyfunction = pyfunction['__init__'].get_object() + pyname = rope.base.pynames.UnboundName(pyobjects.PyObject(pyclass)) + args = self._args_with_self(primary, pyname, pyfunction, node) + elif '__call__' in pyfunction: + pyfunction = pyfunction['__call__'].get_object() + args = self._args_with_self(primary, pyname, pyfunction, node) + else: + return + self._call(pyfunction, args) + + def _args_with_self(self, primary, self_pyname, pyfunction, node): + base_args = arguments.create_arguments(primary, pyfunction, + node, self.scope) + return arguments.MixedArguments(self_pyname, base_args, self.scope) + + def _call(self, pyfunction, args): + if isinstance(pyfunction, pyobjects.PyFunction): + if self.follow is not None: + before = self._parameter_objects(pyfunction) + self.pycore.object_info.function_called( + pyfunction, args.get_arguments(pyfunction.get_param_names())) + pyfunction._set_parameter_pyobjects(None) + if self.follow is not None: + after = self._parameter_objects(pyfunction) + if after != before: + self.follow(pyfunction) + # XXX: Maybe we should not call every builtin function + if isinstance(pyfunction, rope.base.builtins.BuiltinFunction): + pyfunction.get_returned_object(args) + + def _parameter_objects(self, pyfunction): + result = [] + for i in range(len(pyfunction.get_param_names(False))): + result.append(pyfunction.get_parameter(i)) + return result + + def _Assign(self, node): + for child in rope.base.ast.get_child_nodes(node): + rope.base.ast.walk(child, self) + visitor = _SOAAssignVisitor() + nodes = [] + for child in node.targets: + rope.base.ast.walk(child, visitor) + nodes.extend(visitor.nodes) + for subscript, levels in nodes: + instance = evaluate.eval_node(self.scope, subscript.value) + args_pynames = [] + args_pynames.append(evaluate.eval_node(self.scope, + subscript.slice.value)) + value = rope.base.oi.soi._infer_assignment( + rope.base.pynames.AssignmentValue(node.value, levels), + self.pymodule) + args_pynames.append(rope.base.pynames.UnboundName(value)) + if instance is not None and value is not None: + pyobject = instance.get_object() + if '__setitem__' in pyobject: + pyfunction = pyobject['__setitem__'].get_object() + args = arguments.ObjectArguments([instance] + args_pynames) + self._call(pyfunction, args) + # IDEA: handle `__setslice__`, too + + +class _SOAAssignVisitor(astutils._NodeNameCollector): + + def __init__(self): + super(_SOAAssignVisitor, self).__init__() + self.nodes = [] + + def _added(self, node, levels): + if isinstance(node, rope.base.ast.Subscript) and \ + isinstance(node.slice, rope.base.ast.Index): + self.nodes.append((node, levels)) diff --git a/venv/Lib/site-packages/rope/base/oi/soi.py b/venv/Lib/site-packages/rope/base/oi/soi.py new file mode 100644 index 0000000000000000000000000000000000000000..c05aba94b1a3c266870ac2af34d0c773430fb3c1 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/soi.py @@ -0,0 +1,222 @@ +"""A module for inferring objects + +For more information see the documentation in `rope.base.oi` +package. + +""" +import rope.base.builtins +import rope.base.pynames +import rope.base.pyobjects +from rope.base import evaluate, utils, arguments +from rope.base.oi.type_hinting.factory import get_type_hinting_factory + + +_ignore_inferred = utils.ignore_exception( + rope.base.pyobjects.IsBeingInferredError) + + +@_ignore_inferred +def infer_returned_object(pyfunction, args): + """Infer the `PyObject` this `PyFunction` returns after calling""" + object_info = pyfunction.pycore.object_info + result = object_info.get_exact_returned(pyfunction, args) + if result is not None: + return result + result = _infer_returned(pyfunction, args) + if result is not None: + if args and pyfunction.get_module().get_resource() is not None: + params = args.get_arguments( + pyfunction.get_param_names(special_args=False)) + object_info.function_called(pyfunction, params, result) + return result + result = object_info.get_returned(pyfunction, args) + if result is not None: + return result + hint_return = get_type_hinting_factory(pyfunction.pycore.project).make_return_provider() + type_ = hint_return(pyfunction) + if type_ is not None: + return rope.base.pyobjects.PyObject(type_) + + +@_ignore_inferred +def infer_parameter_objects(pyfunction): + """Infer the `PyObject`\s of parameters of this `PyFunction`""" + object_info = pyfunction.pycore.object_info + result = object_info.get_parameter_objects(pyfunction) + if result is None: + result = _parameter_objects(pyfunction) + _handle_first_parameter(pyfunction, result) + return result + + +def _handle_first_parameter(pyobject, parameters): + kind = pyobject.get_kind() + if parameters is None or kind not in ['method', 'classmethod']: + pass + if not parameters: + if not pyobject.get_param_names(special_args=False): + return + parameters.append(rope.base.pyobjects.get_unknown()) + if kind == 'method': + parameters[0] = rope.base.pyobjects.PyObject(pyobject.parent) + if kind == 'classmethod': + parameters[0] = pyobject.parent + + +@_ignore_inferred +def infer_assigned_object(pyname): + if not pyname.assignments: + return + for assignment in reversed(pyname.assignments): + result = _infer_assignment(assignment, pyname.module) + if isinstance(result, rope.base.builtins.BuiltinUnknown) and result.get_name() == 'NotImplementedType': + break + elif result == rope.base.pyobjects.get_unknown(): + break + elif result is not None: + return result + + hint_assignment = get_type_hinting_factory(pyname.module.pycore.project).make_assignment_provider() + hinting_result = hint_assignment(pyname) + if hinting_result is not None: + return rope.base.pyobjects.PyObject(hinting_result) + return result + + +def get_passed_objects(pyfunction, parameter_index): + object_info = pyfunction.pycore.object_info + result = object_info.get_passed_objects(pyfunction, + parameter_index) + if not result: + statically_inferred = _parameter_objects(pyfunction) + if len(statically_inferred) > parameter_index: + result.append(statically_inferred[parameter_index]) + return result + + +def _infer_returned(pyobject, args): + if args: + # HACK: Setting parameter objects manually + # This is not thread safe and might cause problems if `args` + # does not come from a good call site + pyobject.get_scope().invalidate_data() + pyobject._set_parameter_pyobjects( + args.get_arguments(pyobject.get_param_names(special_args=False))) + scope = pyobject.get_scope() + if not scope._get_returned_asts(): + return + maxtries = 3 + for returned_node in reversed(scope._get_returned_asts()[-maxtries:]): + try: + resulting_pyname = evaluate.eval_node(scope, returned_node) + if resulting_pyname is None: + continue + pyobject = resulting_pyname.get_object() + if pyobject == rope.base.pyobjects.get_unknown(): + continue + if not scope._is_generator(): + return pyobject + else: + return rope.base.builtins.get_generator(pyobject) + except rope.base.pyobjects.IsBeingInferredError: + pass + + +def _parameter_objects(pyobject): + result = [] + params = pyobject.get_param_names(special_args=False) + hint_param = get_type_hinting_factory(pyobject.pycore.project).make_param_provider() + for name in params: + type_ = hint_param(pyobject, name) + if type_ is not None: + result.append(rope.base.pyobjects.PyObject(type_)) + else: + result.append(rope.base.pyobjects.get_unknown()) + return result + +# handling `rope.base.pynames.AssignmentValue` + + +@_ignore_inferred +def _infer_assignment(assignment, pymodule): + result = _follow_pyname(assignment, pymodule) + if result is None: + return None + pyname, pyobject = result + pyobject = _follow_evaluations(assignment, pyname, pyobject) + if pyobject is None: + return None + return _follow_levels(assignment, pyobject) + + +def _follow_levels(assignment, pyobject): + for index in assignment.levels: + if isinstance(pyobject.get_type(), rope.base.builtins.Tuple): + holdings = pyobject.get_type().get_holding_objects() + if holdings: + pyobject = holdings[min(len(holdings) - 1, index)] + else: + pyobject = None + elif isinstance(pyobject.get_type(), rope.base.builtins.List): + pyobject = pyobject.get_type().holding + else: + pyobject = None + if pyobject is None: + break + return pyobject + + +@_ignore_inferred +def _follow_pyname(assignment, pymodule, lineno=None): + assign_node = assignment.ast_node + if lineno is None: + lineno = _get_lineno_for_node(assign_node) + holding_scope = pymodule.get_scope().get_inner_scope_for_line(lineno) + pyname = evaluate.eval_node(holding_scope, assign_node) + if pyname is not None: + result = pyname.get_object() + if isinstance(result.get_type(), rope.base.builtins.Property) and \ + holding_scope.get_kind() == 'Class': + arg = rope.base.pynames.UnboundName( + rope.base.pyobjects.PyObject(holding_scope.pyobject)) + return pyname, result.get_type().get_property_object( + arguments.ObjectArguments([arg])) + return pyname, result + + +@_ignore_inferred +def _follow_evaluations(assignment, pyname, pyobject): + new_pyname = pyname + tokens = assignment.evaluation.split('.') + for token in tokens: + call = token.endswith('()') + if call: + token = token[:-2] + if token: + pyname = new_pyname + new_pyname = _get_attribute(pyobject, token) + if new_pyname is not None: + pyobject = new_pyname.get_object() + if pyobject is not None and call: + if isinstance(pyobject, rope.base.pyobjects.AbstractFunction): + args = arguments.ObjectArguments([pyname]) + pyobject = pyobject.get_returned_object(args) + else: + pyobject = None + if pyobject is None: + break + if pyobject is not None and assignment.assign_type: + return rope.base.pyobjects.PyObject(pyobject) + return pyobject + + +def _get_lineno_for_node(assign_node): + if hasattr(assign_node, 'lineno') and \ + assign_node.lineno is not None: + return assign_node.lineno + return 1 + + +def _get_attribute(pyobject, name): + if pyobject is not None and name in pyobject: + return pyobject[name] diff --git a/venv/Lib/site-packages/rope/base/oi/transform.py b/venv/Lib/site-packages/rope/base/oi/transform.py new file mode 100644 index 0000000000000000000000000000000000000000..aa29c373d5cb34e24994ed2e43c86f0a080276a0 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/transform.py @@ -0,0 +1,285 @@ +"""Provides classes for persisting `PyObject`\s""" +import os +import re + +import rope.base.builtins +from rope.base import exceptions + + +class PyObjectToTextual(object): + """For transforming `PyObject` to textual form + + This can be used for storing `PyObjects` in files. Use + `TextualToPyObject` for converting back. + + """ + + def __init__(self, project): + self.project = project + + def transform(self, pyobject): + """Transform a `PyObject` to textual form""" + if pyobject is None: + return ('none',) + object_type = type(pyobject) + try: + method = getattr(self, object_type.__name__ + '_to_textual') + return method(pyobject) + except AttributeError: + return ('unknown',) + + def __call__(self, pyobject): + return self.transform(pyobject) + + def PyObject_to_textual(self, pyobject): + if isinstance(pyobject.get_type(), rope.base.pyobjects.AbstractClass): + result = self.transform(pyobject.get_type()) + if result[0] == 'defined': + return ('instance', result) + return result + return ('unknown',) + + def PyFunction_to_textual(self, pyobject): + return self._defined_to_textual(pyobject) + + def PyClass_to_textual(self, pyobject): + return self._defined_to_textual(pyobject) + + def _defined_to_textual(self, pyobject): + address = [] + while pyobject.parent is not None: + address.insert(0, pyobject.get_name()) + pyobject = pyobject.parent + return ('defined', self._get_pymodule_path(pyobject.get_module()), + '.'.join(address)) + + def PyModule_to_textual(self, pyobject): + return ('defined', self._get_pymodule_path(pyobject)) + + def PyPackage_to_textual(self, pyobject): + return ('defined', self._get_pymodule_path(pyobject)) + + def List_to_textual(self, pyobject): + return ('builtin', 'list', self.transform(pyobject.holding)) + + def Dict_to_textual(self, pyobject): + return ('builtin', 'dict', self.transform(pyobject.keys), + self.transform(pyobject.values)) + + def Tuple_to_textual(self, pyobject): + objects = [self.transform(holding) + for holding in pyobject.get_holding_objects()] + return tuple(['builtin', 'tuple'] + objects) + + def Set_to_textual(self, pyobject): + return ('builtin', 'set', self.transform(pyobject.holding)) + + def Iterator_to_textual(self, pyobject): + return ('builtin', 'iter', self.transform(pyobject.holding)) + + def Generator_to_textual(self, pyobject): + return ('builtin', 'generator', self.transform(pyobject.holding)) + + def Str_to_textual(self, pyobject): + return ('builtin', 'str') + + def File_to_textual(self, pyobject): + return ('builtin', 'file') + + def BuiltinFunction_to_textual(self, pyobject): + return ('builtin', 'function', pyobject.get_name()) + + def _get_pymodule_path(self, pymodule): + return self.resource_to_path(pymodule.get_resource()) + + def resource_to_path(self, resource): + if resource.project == self.project: + return resource.path + else: + return resource.real_path + + +class TextualToPyObject(object): + """For transforming textual form to `PyObject`""" + + def __init__(self, project, allow_in_project_absolutes=False): + self.project = project + + def __call__(self, textual): + return self.transform(textual) + + def transform(self, textual): + """Transform an object from textual form to `PyObject`""" + if textual is None: + return None + type = textual[0] + try: + method = getattr(self, type + '_to_pyobject') + return method(textual) + except AttributeError: + return None + + def builtin_to_pyobject(self, textual): + method = getattr(self, 'builtin_%s_to_pyobject' % textual[1], None) + if method is not None: + return method(textual) + + def builtin_str_to_pyobject(self, textual): + return rope.base.builtins.get_str() + + def builtin_list_to_pyobject(self, textual): + holding = self.transform(textual[2]) + return rope.base.builtins.get_list(holding) + + def builtin_dict_to_pyobject(self, textual): + keys = self.transform(textual[2]) + values = self.transform(textual[3]) + return rope.base.builtins.get_dict(keys, values) + + def builtin_tuple_to_pyobject(self, textual): + objects = [] + for holding in textual[2:]: + objects.append(self.transform(holding)) + return rope.base.builtins.get_tuple(*objects) + + def builtin_set_to_pyobject(self, textual): + holding = self.transform(textual[2]) + return rope.base.builtins.get_set(holding) + + def builtin_iter_to_pyobject(self, textual): + holding = self.transform(textual[2]) + return rope.base.builtins.get_iterator(holding) + + def builtin_generator_to_pyobject(self, textual): + holding = self.transform(textual[2]) + return rope.base.builtins.get_generator(holding) + + def builtin_file_to_pyobject(self, textual): + return rope.base.builtins.get_file() + + def builtin_function_to_pyobject(self, textual): + if textual[2] in rope.base.builtins.builtins: + return rope.base.builtins.builtins[textual[2]].get_object() + + def unknown_to_pyobject(self, textual): + return None + + def none_to_pyobject(self, textual): + return None + + def _module_to_pyobject(self, textual): + path = textual[1] + return self._get_pymodule(path) + + def _hierarchical_defined_to_pyobject(self, textual): + path = textual[1] + names = textual[2].split('.') + pymodule = self._get_pymodule(path) + pyobject = pymodule + for name in names: + if pyobject is None: + return None + if isinstance(pyobject, rope.base.pyobjects.PyDefinedObject): + try: + pyobject = pyobject.get_scope()[name].get_object() + except exceptions.NameNotFoundError: + return None + else: + return None + return pyobject + + def defined_to_pyobject(self, textual): + if len(textual) == 2 or textual[2] == '': + return self._module_to_pyobject(textual) + else: + return self._hierarchical_defined_to_pyobject(textual) + + def instance_to_pyobject(self, textual): + type = self.transform(textual[1]) + if type is not None: + return rope.base.pyobjects.PyObject(type) + + def _get_pymodule(self, path): + resource = self.path_to_resource(path) + if resource is not None: + return self.project.get_pymodule(resource) + + def path_to_resource(self, path): + try: + root = self.project.address + if not os.path.isabs(path): + return self.project.get_resource(path) + if path == root or path.startswith(root + os.sep): + # INFO: This is a project file; should not be absolute + return None + import rope.base.project + return rope.base.project.get_no_project().get_resource(path) + except exceptions.ResourceNotFoundError: + return None + + +class DOITextualToPyObject(TextualToPyObject): + """For transforming textual form to `PyObject` + + The textual form DOI uses is different from rope's standard + textual form. The reason is that we cannot find the needed + information by analyzing live objects. This class can be + used to transform DOI textual form to `PyObject` and later + we can convert it to standard textual form using + `TextualToPyObject` class. + + """ + + def _function_to_pyobject(self, textual): + path = textual[1] + lineno = int(textual[2]) + pymodule = self._get_pymodule(path) + if pymodule is not None: + scope = pymodule.get_scope() + inner_scope = scope.get_inner_scope_for_line(lineno) + return inner_scope.pyobject + + def _class_to_pyobject(self, textual): + path, name = textual[1:] + pymodule = self._get_pymodule(path) + if pymodule is None: + return None + module_scope = pymodule.get_scope() + suspected = None + if name in module_scope.get_names(): + suspected = module_scope[name].get_object() + if suspected is not None and \ + isinstance(suspected, rope.base.pyobjects.PyClass): + return suspected + else: + lineno = self._find_occurrence(name, + pymodule.get_resource().read()) + if lineno is not None: + inner_scope = module_scope.get_inner_scope_for_line(lineno) + return inner_scope.pyobject + + def defined_to_pyobject(self, textual): + if len(textual) == 2: + return self._module_to_pyobject(textual) + else: + if textual[2].isdigit(): + result = self._function_to_pyobject(textual) + else: + result = self._class_to_pyobject(textual) + if not isinstance(result, rope.base.pyobjects.PyModule): + return result + + def _find_occurrence(self, name, source): + pattern = re.compile(r'^\s*class\s*' + name + r'\b') + lines = source.split('\n') + for i in range(len(lines)): + if pattern.match(lines[i]): + return i + 1 + + def path_to_resource(self, path): + import rope.base.libutils + relpath = rope.base.libutils.path_relative_to_project_root( + self.project, path) + if relpath is not None: + path = relpath + return super(DOITextualToPyObject, self).path_to_resource(path) diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/__init__.py b/venv/Lib/site-packages/rope/base/oi/type_hinting/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/type_hinting/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e8722b217f5ea068c434366a8418b743a804caf Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/type_hinting/__pycache__/__init__.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/__pycache__/evaluate.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/type_hinting/__pycache__/evaluate.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..36cc9741faeb3f0fad49440dccdd47f55b6477a5 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/type_hinting/__pycache__/evaluate.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/__pycache__/factory.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/type_hinting/__pycache__/factory.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7f170d75b0678dbb2b346a69179a8e6f16a4da35 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/type_hinting/__pycache__/factory.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/__pycache__/interfaces.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/type_hinting/__pycache__/interfaces.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..04347dc43926fde2656b4ade1c7cd37ada5d65d0 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/type_hinting/__pycache__/interfaces.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/__pycache__/utils.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/type_hinting/__pycache__/utils.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6d833c25fec6bba308066c2da255fab18f0c1804 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/type_hinting/__pycache__/utils.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/evaluate.py b/venv/Lib/site-packages/rope/base/oi/type_hinting/evaluate.py new file mode 100644 index 0000000000000000000000000000000000000000..3b82eb07968e5a1d723f03c2d27fbd1b3bab6df0 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/type_hinting/evaluate.py @@ -0,0 +1,353 @@ +# Based on super lightweight Simple Top-Down Parser from http://effbot.org/zone/simple-top-down-parsing.htm +# and https://bitbucket.org/emacsway/sqlbuilder/src/default/sqlbuilder/smartsql/contrib/evaluate.py +import re +from rope.base.utils import pycompat +from rope.base.oi.type_hinting import utils +from rope.base import utils as base_utils + + +class SymbolBase(object): + + name = None # node/token type name + + def __init__(self): + self.value = None # used by name and literals + self.first = None + self.second = None + self.third = None # used by tree nodes + + def nud(self, parser): + raise SyntaxError( + "Syntax error (%r)." % self.name + ) + + def led(self, left, parser): + raise SyntaxError( + "Unknown operator (%r)." % self.name + ) + + def evaluate(self, pyobject): + raise NotImplementedError(self.name, self) + + def __repr__(self): + if self.name == '(name)': + return "(%s %s)" % (self.name[1:-1], self.value) + out = [repr(self.name), self.first, self.second, self.third] + out = [str(i) for i in out if i] + return '(' + ' '.join(out) + ')' + + +class SymbolTable(object): + + def multi(func): + def _inner(self, names, *a, **kw): + for name in names.split(): + func(self, name, *a, **kw) + return _inner + + def __init__(self): + self.symbol_table = {} + + def get(self, name, default=None): + return self.symbol_table.get(name, default) + + def __getitem__(self, name): + return self.symbol_table[name] + + def __iter__(self): + return iter(self.symbol_table) + + def symbol(self, name, bp=0): + try: + s = self.symbol_table[name] + except KeyError: + + class S(SymbolBase): + pass + + s = S + s.__name__ = "symbol-" + name # for debugging + s.name = name + s.lbp = bp + self.symbol_table[name] = s + else: + s.lbp = max(bp, s.lbp) + return s + + @multi + def infix(self, name, bp): + symbol = self.symbol(name, bp) + + @method(symbol) + def led(self, left, parser): + self.first = left + self.second = parser.expression(bp) + return self + + @multi + def infix_r(self, name, bp): + symbol = self.symbol(name, bp) + + @method(symbol) + def led(self, left, parser): + self.first = left + self.second = parser.expression(bp - 0.1) + return self + + def ternary(self, name, name2, bp): + symbol = self.symbol(name, bp) + symbol2 = self.symbol(name2) + + @method(symbol) + def led(self, left, parser): + self.first = left + self.second = parser.expression(symbol2.lbp) + parser.advance(symbol2.name) + self.third = parser.expression(symbol2.lbp + 0.1) + return self + + @multi + def prefix(self, name, bp): + symbol = self.symbol(name, bp) + + @method(symbol) + def nud(self, parser): + self.first = parser.expression(bp) + return self + + @multi + def postfix(self, name, bp): + symbol = self.symbol(name, bp) + + @method(symbol) + def led(self, left, parser): + self.first = left + return self + + multi = staticmethod(multi) # Just for code checker + +symbol_table = SymbolTable() + + +class Lexer(object): + + _token_pattern = re.compile(r""" + \s* + (?: + ( + [,()\[\]|] + | -> + | (?<=\s)(?:or)\b + ) # operator + | ([a-zA-Z](?:\w|\.)*) # name + ) + """, re.U | re.S | re.X) + + def __init__(self, symbol_table): + self.symbol_table = symbol_table + + def tokenize(self, program): + for name, value in self._tokenize_expr(program): + symbol = symbol_table.get(value) + if symbol: + s = symbol() + elif name == "(name)": + symbol = symbol_table[name] + s = symbol() + s.value = value + else: + raise SyntaxError("Unknown operator ({0}). Possible operators are {1!r}".format( + value, list(self.symbol_table) + )) + + yield s + + def _tokenize_expr(self, program): + if isinstance(program, bytes): + program = program.decode('utf-8') + # import pprint; pprint.pprint(self._token_pattern.findall(program)) + for operator, name in self._token_pattern.findall(program): + if operator: + yield '(operator)', operator + elif name: + yield '(name)', name + else: + raise SyntaxError + yield '(end)', '(end)' + + +class Parser(object): + + token = None + next = None + + def __init__(self, lexer): + self.lexer = lexer + + def parse(self, program): + generator = self.lexer.tokenize(program) + try: + self.next = generator.__next__ # PY3 + except AttributeError: + self.next = generator.next + self.token = self.next() + return self.expression() + + def expression(self, rbp=0): + t = self.token + self.token = self.next() + left = t.nud(self) + while rbp < self.token.lbp: + t = self.token + self.token = self.next() + left = t.led(left, self) + return left + + def advance(self, name=None): + if name and self.token.name != name: + raise SyntaxError("Expected {0!r} but found {1!r}".format(name, self.token.name)) + self.token = self.next() + + +def method(s): + assert issubclass(s, SymbolBase) + + def bind(fn): + setattr(s, fn.__name__, fn) + return fn + + return bind + +symbol, infix, infix_r, prefix, postfix, ternary = ( + symbol_table.symbol, symbol_table.infix, symbol_table.infix_r, symbol_table.prefix, + symbol_table.postfix, symbol_table.ternary +) + +symbol('(', 270) +symbol(')') +symbol('[', 250) # Parameters +symbol(']') +symbol('->', 230) +infix('|', 170) +infix('or', 170) +symbol(',') + +symbol('(name)') +symbol('(end)') + + +@method(symbol('(name)')) +def nud(self, parser): + return self + + +@method(symbol('(name)')) +def evaluate(self, pyobject): + return utils.resolve_type(self.value, pyobject) + + +# Parametrized objects +@method(symbol('[')) +def led(self, left, parser): + self.first = left + self.second = [] + if parser.token.name != ']': + while 1: + if parser.token.name == ']': + break + self.second.append(parser.expression()) + if parser.token.name != ',': + break + parser.advance(',') + parser.advance(']') + return self + + +@method(symbol('[')) +def evaluate(self, pyobject): + return utils.parametrize_type( + self.first.evaluate(pyobject), + *[i.evaluate(pyobject) for i in self.second] + ) + + +# Anonymous Function Calls +@method(symbol('(')) +def nud(self, parser): + self.second = [] + if parser.token.name != ')': + while 1: + self.second.append(parser.expression()) + if parser.token.name != ',': + break + parser.advance(',') + parser.advance(')') + parser.advance('->') + self.third = parser.expression(symbol('->').lbp + 0.1) + return self + + +# Function Calls +@method(symbol('(')) +def led(self, left, parser): + self.first = left + self.second = [] + if parser.token.name != ')': + while 1: + self.second.append(parser.expression()) + if parser.token.name != ',': + break + parser.advance(',') + parser.advance(')') + parser.advance('->') + self.third = parser.expression(symbol('->').lbp + 0.1) + return self + + +@method(symbol('(')) +def evaluate(self, pyobject): + # TODO: Implement me + raise NotImplementedError + + +@method(symbol('or')) +@method(symbol('|')) +def evaluate(self, pyobject): + # TODO: Implement me + raise NotImplementedError + + +class Compiler(object): + + parser_factory = Parser + lexer_factory = Lexer + symbol_table = symbol_table + + def _make_parser(self): + return self.parser_factory(self.lexer_factory(self.symbol_table)) + + @base_utils.cached(500) + def __call__(self, program): + """ + :type program: str + :rtype: rope.base.oi.type_hinting.evaluate.SymbolBase + """ + return self._make_parser().parse(program) + +compile = Compiler() + + +class Evaluator(object): + + compile = compile + + def __call__(self, program, pyobject): + """Evaluates the program string or AST + + :type program: str or rope.base.oi.type_hinting.evaluate.SymbolBase + :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None + """ + ast = self.compile(program) if isinstance(program, pycompat.string_types) else program + return ast.evaluate(pyobject) + +evaluate = Evaluator() diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/factory.py b/venv/Lib/site-packages/rope/base/oi/type_hinting/factory.py new file mode 100644 index 0000000000000000000000000000000000000000..644d12c0325a00c7e41f7da2b8efaed72368f031 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/type_hinting/factory.py @@ -0,0 +1,70 @@ +from rope.base.oi.type_hinting import interfaces +from rope.base.oi.type_hinting.providers import ( + composite, inheritance, docstrings, numpydocstrings, pep0484_type_comments +) +from rope.base.oi.type_hinting.resolvers import composite as composite_resolvers, types +from rope.base import utils + + +class TypeHintingFactory(interfaces.ITypeHintingFactory): + + @utils.saveit + def make_param_provider(self): + providers = [ + docstrings.ParamProvider(docstrings.DocstringParamParser(), self.make_resolver()), + docstrings.ParamProvider(numpydocstrings.NumPyDocstringParamParser(), self.make_resolver()), + ] + return inheritance.ParamProvider(composite.ParamProvider(*providers)) + + @utils.saveit + def make_return_provider(self): + providers = [ + docstrings.ReturnProvider(docstrings.DocstringReturnParser(), self.make_resolver()), + ] + return inheritance.ReturnProvider(composite.ReturnProvider(*providers)) + + @utils.saveit + def make_assignment_provider(self): + providers = [ + pep0484_type_comments.AssignmentProvider(self.make_resolver()), + docstrings.AssignmentProvider(docstrings.DocstringParamParser(), self.make_resolver()), + docstrings.AssignmentProvider(numpydocstrings.NumPyDocstringParamParser(), self.make_resolver()), + ] + return inheritance.AssignmentProvider(composite.AssignmentProvider(*providers)) + + @utils.saveit + def make_resolver(self): + """ + :rtype: rope.base.oi.type_hinting.resolvers.interfaces.IResolver + """ + resolvers = [ + types.Resolver(), + ] + return composite_resolvers.Resolver(*resolvers) + + +default_type_hinting_factory = TypeHintingFactory() + + +class TypeHintingFactoryAccessor(object): + + def __call__(self, project): + """ + :type project: rope.base.project.Project + :rtype: rope.base.oi.type_hinting.interfaces.ITypeHintingFactory + """ + factory_location = project.get_prefs().get( + 'type_hinting_factory', + 'rope.base.oi.type_hinting.factory.default_type_hinting_factory' + ) + return self._get_factory(factory_location) + + @utils.cached(10) + def _get_factory(self, factory_location): + """ + :type factory_location: str + :rtype: rope.base.oi.type_hinting.interfaces.ITypeHintingFactory + """ + return utils.resolve(factory_location) + +get_type_hinting_factory = TypeHintingFactoryAccessor() diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/interfaces.py b/venv/Lib/site-packages/rope/base/oi/type_hinting/interfaces.py new file mode 100644 index 0000000000000000000000000000000000000000..fc5568f32d53778757e335046cb05c36adf6652c --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/type_hinting/interfaces.py @@ -0,0 +1,25 @@ +class ITypeHintingFactory(object): + + def make_param_provider(self): + """ + :rtype: rope.base.oi.type_hinting.providers.interfaces.IParamProvider + """ + raise NotImplementedError + + def make_return_provider(self): + """ + :rtype: rope.base.oi.type_hinting.providers.interfaces.IReturnProvider + """ + raise NotImplementedError + + def make_assignment_provider(self): + """ + :rtype: rope.base.oi.type_hinting.providers.interfaces.IAssignmentProvider + """ + raise NotImplementedError + + def make_resolver(self): + """ + :rtype: rope.base.oi.type_hinting.resolvers.interfaces.IResolver + """ + raise NotImplementedError diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__init__.py b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9fc6ccb5f6ef3f478885f46057b1bea9f2fb2940 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/__init__.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/composite.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/composite.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f546796991bde41ebfbeeb2379bcc3fc68403ef Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/composite.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/docstrings.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/docstrings.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e08180ce9bc486dbd42ab9b11f6f9213fd989140 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/docstrings.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/inheritance.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/inheritance.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cf370bdc9b26a23422f2869efd3ca35eecc8ec44 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/inheritance.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/interfaces.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/interfaces.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..76b3d9d26425ad43a237f67123ca7f4940103b24 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/interfaces.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/numpydocstrings.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/numpydocstrings.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e0020fbb08ec117b265b10305d69edb860ae9aae Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/numpydocstrings.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/pep0484_type_comments.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/pep0484_type_comments.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5c327621b8975e2fb4a9c82fe8abce57393749fa Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/__pycache__/pep0484_type_comments.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/composite.py b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/composite.py new file mode 100644 index 0000000000000000000000000000000000000000..34a9fae7b5fd212925ea3acd72fda9f9450d005c --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/composite.py @@ -0,0 +1,59 @@ +from rope.base.oi.type_hinting.providers import interfaces + + +class ParamProvider(interfaces.IParamProvider): + + def __init__(self, *delegates): + """ + :type delegates: list[rope.base.oi.type_hinting.providers.interfaces.IParamProvider] + """ + self._delegates = delegates + + def __call__(self, pyfunc, param_name): + """ + :type pyfunc: rope.base.pyobjectsdef.PyFunction + :type param_name: str + :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None + """ + for delegate in self._delegates: + result = delegate(pyfunc, param_name) + if result: + return result + + +class ReturnProvider(interfaces.IReturnProvider): + + def __init__(self, *delegates): + """ + :type delegates: list[rope.base.oi.type_hinting.providers.interfaces.IReturnProvider] + """ + self._delegates = delegates + + def __call__(self, pyfunc): + """ + :type pyfunc: rope.base.pyobjectsdef.PyFunction + :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None + """ + for delegate in self._delegates: + result = delegate(pyfunc) + if result: + return result + + +class AssignmentProvider(interfaces.IAssignmentProvider): + + def __init__(self, *delegates): + """ + :type delegates: list[rope.base.oi.type_hinting.providers.interfaces.IAssignmentProvider] + """ + self._delegates = delegates + + def __call__(self, pyname): + """ + :type pyname: rope.base.pynamesdef.AssignedName + :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None + """ + for delegate in self._delegates: + result = delegate(pyname) + if result: + return result diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/docstrings.py b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/docstrings.py new file mode 100644 index 0000000000000000000000000000000000000000..8d52e6fbfa93bf559238b1af7e46e36743f36310 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/docstrings.py @@ -0,0 +1,193 @@ +""" +Hinting the type using docstring of class/function. + +It's an irreplaceable thing if you are using Dependency Injection with passive class: +http://www.martinfowler.com/articles/injection.html + +Some code extracted (or based on code) from: +https://github.com/davidhalter/jedi/blob/b489019f5bd5750051122b94cc767df47751ecb7/jedi/evaluate/docstrings.py +Thanks to @davidhalter for this utils under MIT License. + +Similar solutions: + + - https://www.jetbrains.com/pycharm/help/type-hinting-in-pycharm.html + - https://www.python.org/dev/peps/pep-0484/#type-comments + - http://www.pydev.org/manual_adv_type_hints.html + - https://jedi.readthedocs.org/en/latest/docs/features.html#type-hinting + +Discussions: + + - https://groups.google.com/d/topic/rope-dev/JlAzmZ83K1M/discussion + - https://groups.google.com/d/topic/rope-dev/LCFNN98vckI/discussion + +""" +import re + +from rope.base.oi.type_hinting import utils +from rope.base.oi.type_hinting.providers import interfaces + + +class ParamProvider(interfaces.IParamProvider): + + def __init__(self, docstring_parser, resolver): + """ + :type docstring_parser: rope.base.oi.type_hinting.providers.docstrings.IParamParser + :type resolver: rope.base.oi.type_hinting.resolvers.interfaces.IResolver + """ + self._parse_docstring = docstring_parser + self._resolve = resolver + + def __call__(self, pyfunc, param_name): + """ + :type pyfunc: rope.base.pyobjectsdef.PyFunction + :type param_name: str + :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None + """ + type_strs = self._parse_docstring(pyfunc.get_doc(), param_name) + if type_strs: + return self._resolve(type_strs[0], pyfunc) + + +class ReturnProvider(interfaces.IReturnProvider): + + def __init__(self, docstring_parser, resolver): + """ + :type docstring_parser: rope.base.oi.type_hinting.providers.docstrings.IReturnParser + :type resolver: rope.base.oi.type_hinting.resolvers.interfaces.IResolver + """ + self._parse_docstring = docstring_parser + self._resolve = resolver + + def __call__(self, pyfunc): + """ + :type pyfunc: rope.base.pyobjectsdef.PyFunction + :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None + """ + type_strs = self._parse_docstring(pyfunc.get_doc()) + if type_strs: + return self._resolve(type_strs[0], pyfunc) + + +class AssignmentProvider(interfaces.IAssignmentProvider): + + def __init__(self, docstring_parser, resolver): + """ + :type docstring_parser: rope.base.oi.type_hinting.providers.docstrings.IParamParser + :type resolver: rope.base.oi.type_hinting.resolvers.interfaces.IResolver + """ + self._parse_docstring = docstring_parser + self._resolve = resolver + + def __call__(self, pyname): + """ + :type pyname: rope.base.pynamesdef.AssignedName + :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None + """ + try: + pyclass, attr_name = utils.get_class_with_attr_name(pyname) + except TypeError: + return + else: + type_strs = self._parse_docstring(pyclass.get_doc(), attr_name) + if type_strs: + return self._resolve(type_strs[0], pyclass) + + +class IParamParser(object): + + def __call__(self, docstring, param_name): + """ + :type docstring: str + :type param_name: str + """ + + +class IReturnParser(object): + + def __call__(self, docstring): + """ + :type docstring: str + """ + + +class DocstringParamParser(IParamParser): + + DOCSTRING_PARAM_PATTERNS = [ + r'\s*:type\s+%s:\s*([^\n]+)', # Sphinx + r'\s*:param\s+(\w+)\s+%s:[^\n]+', # Sphinx param with type + r'\s*@type\s+%s:\s*([^\n]+)', # Epydoc + ] + + def __init__(self): + self._strip_rst_role = RSTRoleStrip() + + def __call__(self, docstring, param_name): + """Search `docstring` for type(-s) of `param_name`. + + >>> DocstringParamParser()(':type param: int', 'param') + ['int'] + >>> DocstringParamParser()('@type param: int', 'param') + ['int'] + >>> DocstringParamParser()(':type param: :class:`threading.Thread`', 'param') + ['threading.Thread'] + >>> bool(DocstringParamParser()('no document', 'param')) + False + >>> DocstringParamParser()(':param int param: some description', 'param') + ['int'] + """ + if not docstring: + return [] + patterns = [re.compile(p % re.escape(param_name)) + for p in self.DOCSTRING_PARAM_PATTERNS] + for pattern in patterns: + match = pattern.search(docstring) + if match: + return [self._strip_rst_role(match.group(1))] + + return [] + + +class DocstringReturnParser(IReturnParser): + + DOCSTRING_RETURN_PATTERNS = [ + re.compile(r'\s*:rtype:\s*([^\n]+)', re.M), # Sphinx + re.compile(r'\s*@rtype:\s*([^\n]+)', re.M), # Epydoc + ] + + def __init__(self): + self._strip_rst_role = RSTRoleStrip() + + def __call__(self, docstring): + if not docstring: + return [] + for p in self.DOCSTRING_RETURN_PATTERNS: + match = p.search(docstring) + if match: + return [self._strip_rst_role(match.group(1))] + return [] + + +class RSTRoleStrip(object): + + RST_ROLE_PATTERN = re.compile(r':[^`]+:`([^`]+)`') + + def __call__(self, type_str): + """ + Strip off the part looks like a ReST role in `type_str`. + + >>> RSTRoleStrip()(':class:`ClassName`') # strip off :class: + 'ClassName' + >>> RSTRoleStrip()(':py:obj:`module.Object`') # works with domain + 'module.Object' + >>> RSTRoleStrip()('ClassName') # do nothing when not ReST role + 'ClassName' + + See also: + http://sphinx-doc.org/domains.html#cross-referencing-python-objects + + """ + match = self.RST_ROLE_PATTERN.match(type_str) + if match: + return match.group(1) + else: + return type_str diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/inheritance.py b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/inheritance.py new file mode 100644 index 0000000000000000000000000000000000000000..cbefc43b8968ace9e9db30e389438fbbb3615702 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/inheritance.py @@ -0,0 +1,66 @@ +from rope.base.oi.type_hinting import utils +from rope.base.oi.type_hinting.providers import interfaces + + +class ParamProvider(interfaces.IParamProvider): + + def __init__(self, delegate): + """ + :type delegate: rope.base.oi.type_hinting.providers.interfaces.IParamProvider + """ + self._delegate = delegate + + def __call__(self, pyfunc, param_name): + """ + :type pyfunc: rope.base.pyobjectsdef.PyFunction + :type param_name: str + :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None + """ + superfunc = pyfunc + while superfunc: + result = self._delegate(superfunc, param_name) + if result: + return result + superfunc = utils.get_super_func(superfunc) + + +class ReturnProvider(interfaces.IReturnProvider): + + def __init__(self, delegate): + """ + :type delegate: rope.base.oi.type_hinting.providers.interfaces.IReturnProvider + """ + self._delegate = delegate + + def __call__(self, pyfunc): + """ + :type pyfunc: rope.base.pyobjectsdef.PyFunction + :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None + """ + superfunc = pyfunc + while superfunc: + result = self._delegate(superfunc) + if result: + return result + superfunc = utils.get_super_func(superfunc) + + +class AssignmentProvider(interfaces.IAssignmentProvider): + + def __init__(self, delegate): + """ + :type delegate: rope.base.oi.type_hinting.providers.interfaces.IAssignmentProvider + """ + self._delegate = delegate + + def __call__(self, pyname): + """ + :type pyname: rope.base.pynamesdef.AssignedName + :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None + """ + super_pyname = pyname + while super_pyname: + result = self._delegate(super_pyname) + if result: + return result + super_pyname = utils.get_super_assignment(super_pyname) diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/interfaces.py b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/interfaces.py new file mode 100644 index 0000000000000000000000000000000000000000..595cf82ca4c5ea855b070d48bbbe08a09939d5fe --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/interfaces.py @@ -0,0 +1,37 @@ +class IParamProvider(object): + + def __call__(self, pyfunc, param_name): + """ + :type pyfunc: rope.base.pyobjectsdef.PyFunction + :type param_name: str + :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None + """ + raise NotImplementedError + + +class IReturnProvider(object): + """ + :type resolve: rope.base.oi.type_hinting.resolvers.interfaces.IResolver + """ + resolve = None + + def __call__(self, pyfunc): + """ + :type pyfunc: rope.base.pyobjectsdef.PyFunction + :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None + """ + raise NotImplementedError + + +class IAssignmentProvider(object): + """ + :type resolve: rope.base.oi.type_hinting.resolvers.interfaces.IResolver + """ + resolve = None + + def __call__(self, pyname): + """ + :type pyname: rope.base.pynamesdef.AssignedName + :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None + """ + raise NotImplementedError diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/numpydocstrings.py b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/numpydocstrings.py new file mode 100644 index 0000000000000000000000000000000000000000..74c05900a9f9ac438ce987b7e8fedc91697cf73a --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/numpydocstrings.py @@ -0,0 +1,43 @@ +""" +Some code extracted (or based on code) from: +https://github.com/davidhalter/jedi/blob/b489019f5bd5750051122b94cc767df47751ecb7/jedi/evaluate/docstrings.py +Thanks to @davidhalter for this utils under MIT License. +""" +import re +from ast import literal_eval +from rope.base.oi.type_hinting.providers import docstrings + +try: + from numpydoc.docscrape import NumpyDocString +except ImportError: + NumpyDocString = None + + +class NumPyDocstringParamParser(docstrings.IParamParser): + + def __call__(self, docstring, param_name): + """Search `docstring` (in numpydoc format) for type(-s) of `param_name`.""" + if not docstring: + return [] + params = NumpyDocString(docstring)._parsed_data['Parameters'] + for p_name, p_type, p_descr in params: + if p_name == param_name: + m = re.match('([^,]+(,[^,]+)*?)(,[ ]*optional)?$', p_type) + if m: + p_type = m.group(1) + + if p_type.startswith('{'): + types = set(type(x).__name__ for x in literal_eval(p_type)) + return list(types) + else: + return [p_type] + return [] + + +class _DummyParamParser(docstrings.IParamParser): + def __call__(self, docstring, param_name): + return [] + + +if not NumpyDocString: + NumPyDocstringParamParser = _DummyParamParser diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/pep0484_type_comments.py b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/pep0484_type_comments.py new file mode 100644 index 0000000000000000000000000000000000000000..357d5906a5d2dd893e9d556d897b2dc73da54602 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/type_hinting/providers/pep0484_type_comments.py @@ -0,0 +1,42 @@ +import re +from rope.base.oi.type_hinting import utils +from rope.base.oi.type_hinting.providers import interfaces + + +class AssignmentProvider(interfaces.IAssignmentProvider): + + def __init__(self, resolver): + """ + :type resolver: rope.base.oi.type_hinting.resolvers.interfaces.IResolver + """ + self._resolve = resolver + + PEP0484_TYPE_COMMENT_PATTERNS = ( + re.compile(r'type:\s*([^\n]+)'), + ) + + def __call__(self, pyname): + """ + :type pyname: rope.base.pynamesdef.AssignedName + :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None + """ + from rope.base.oi.soi import _get_lineno_for_node + lineno = _get_lineno_for_node(pyname.assignments[0].ast_node) + holding_scope = pyname.module.get_scope().get_inner_scope_for_line(lineno) + line = holding_scope._get_global_scope()._scope_finder.lines.get_line(lineno) + if '#' in line: + type_strs = self._search_type_in_type_comment(line.split('#', 1)[1]) + if type_strs: + return self._resolve(type_strs[0], holding_scope.pyobject) + + def _search_type_in_type_comment(self, code): + """ For more info see: + https://www.python.org/dev/peps/pep-0484/#type-comments + + >>> AssignmentProvider()._search_type_in_type_comment('type: int') + ['int'] + """ + for p in self.PEP0484_TYPE_COMMENT_PATTERNS: + match = p.search(code) + if match: + return [match.group(1)] diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/__init__.py b/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7db9c7e001edb92b319be99f2bba9150b5fd5acb Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/__pycache__/__init__.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/__pycache__/composite.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/__pycache__/composite.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..83dc1c7f9c3000ae2f2dcd19bbe1706d89a6f573 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/__pycache__/composite.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/__pycache__/interfaces.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/__pycache__/interfaces.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..149839607b61820f2631ff8e1c9c6f8aa4a031d3 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/__pycache__/interfaces.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/__pycache__/types.cpython-37.pyc b/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/__pycache__/types.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eba5a9092e38ccc768c20464983b4c824e3b8ebb Binary files /dev/null and b/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/__pycache__/types.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/composite.py b/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/composite.py new file mode 100644 index 0000000000000000000000000000000000000000..4e331e48bafaf05978f7857e7d827da8bddb06fb --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/composite.py @@ -0,0 +1,22 @@ +from rope.base.oi.type_hinting.resolvers import interfaces + + +class Resolver(interfaces.IResolver): + + def __init__(self, *delegates): + """ + :type delegates: list[rope.base.oi.type_hinting.resolvers.interfaces.IResolver] + """ + self._delegates = delegates + + def __call__(self, hint, pyobject): + """ + :param hint: For example "List[int]" or "(Foo, Bar) -> Baz" or simple "Foo" + :type hint: str + :type pyobject: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject + :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None + """ + for delegate in self._delegates: + result = delegate(hint, pyobject) + if result: + return result diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/interfaces.py b/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/interfaces.py new file mode 100644 index 0000000000000000000000000000000000000000..cfcff3d99ebda30b64512fb191bbacc3064dd50b --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/interfaces.py @@ -0,0 +1,10 @@ +class IResolver(object): + + def __call__(self, hint, pyobject): + """ + :param hint: For example "List[int]" or "(Foo, Bar) -> Baz" or simple "Foo" + :type hint: str + :type pyobject: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject + :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None + """ + raise NotImplementedError diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/types.py b/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/types.py new file mode 100644 index 0000000000000000000000000000000000000000..252696a65a9e17cf0103d52ae25ec7cb42db4d69 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/type_hinting/resolvers/types.py @@ -0,0 +1,16 @@ +from rope.base.oi.type_hinting import evaluate +from rope.base.oi.type_hinting.resolvers import interfaces + + +class Resolver(interfaces.IResolver): + def __call__(self, hint, pyobject): + """ + :param hint: For example "List[int]" or "(Foo, Bar) -> Baz" or simple "Foo" + :type hint: str + :type pyobject: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject + :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None + """ + try: + return evaluate.evaluate(hint, pyobject) + except (Exception): + pass diff --git a/venv/Lib/site-packages/rope/base/oi/type_hinting/utils.py b/venv/Lib/site-packages/rope/base/oi/type_hinting/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..aec82ac05132b7698d48c1bfef15e29254939bec --- /dev/null +++ b/venv/Lib/site-packages/rope/base/oi/type_hinting/utils.py @@ -0,0 +1,136 @@ +import rope.base.builtins +import rope.base.utils as base_utils +from rope.base.evaluate import ScopeNameFinder +from rope.base.exceptions import AttributeNotFoundError +from rope.base.pyobjects import PyClass, PyFunction +from rope.base.utils import pycompat + + +def get_super_func(pyfunc): + + if not isinstance(pyfunc.parent, PyClass): + return + + for cls in get_mro(pyfunc.parent)[1:]: + try: + superfunc = cls.get_attribute(pyfunc.get_name()).get_object() + except AttributeNotFoundError: + pass + else: + if isinstance(superfunc, PyFunction): + return superfunc + + +def get_super_assignment(pyname): + """ + :type pyname: rope.base.pynamesdef.AssignedName + :type: rope.base.pynamesdef.AssignedName + """ + try: + pyclass, attr_name = get_class_with_attr_name(pyname) + except TypeError: + return + else: + for super_pyclass in get_mro(pyclass)[1:]: + if attr_name in super_pyclass: + return super_pyclass[attr_name] + + +def get_class_with_attr_name(pyname): + """ + :type pyname: rope.base.pynamesdef.AssignedName + :return: rope.base.pyobjectsdef.PyClass, str + :rtype: tuple + """ + lineno = get_lineno_for_node(pyname.assignments[0].ast_node) + holding_scope = pyname.module.get_scope().get_inner_scope_for_line(lineno) + pyobject = holding_scope.pyobject + if isinstance(pyobject, PyClass): + pyclass = pyobject + elif (isinstance(pyobject, PyFunction) and + isinstance(pyobject.parent, PyClass)): + pyclass = pyobject.parent + else: + return + for name, attr in pyclass.get_attributes().items(): + if attr is pyname: + return (pyclass, name) + + +def get_lineno_for_node(assign_node): + if hasattr(assign_node, 'lineno') and \ + assign_node.lineno is not None: + return assign_node.lineno + return 1 + + +def get_mro(pyclass): + # FIXME: to use real mro() result + l = [pyclass] + for cls in l: + for super_cls in cls.get_superclasses(): + if isinstance(super_cls, PyClass) and super_cls not in l: + l.append(super_cls) + return l + + +def resolve_type(type_name, pyobject): + """ + :type type_name: str + :type pyobject: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject + :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None + """ + if '.' not in type_name: + try: + return pyobject.get_module().get_scope().get_name(type_name).get_object() + except Exception: + pass + else: + mod_name, attr_name = type_name.rsplit('.', 1) + try: + mod_finder = ScopeNameFinder(pyobject.get_module()) + mod = mod_finder._find_module(mod_name).get_object() + return mod.get_attribute(attr_name).get_object() + except Exception: + pass + + +class ParametrizeType(object): + + _supported_mapping = { + 'builtins.list': 'rope.base.builtins.get_list', + 'builtins.tuple': 'rope.base.builtins.get_tuple', + 'builtins.set': 'rope.base.builtins.get_set', + 'builtins.dict': 'rope.base.builtins.get_dict', + '_collections_abc.Iterable': 'rope.base.builtins.get_iterator', + '_collections_abc.Iterator': 'rope.base.builtins.get_iterator', + 'collections.abc.Iterable': 'rope.base.builtins.get_iterator', # Python3.3 + 'collections.abc.Iterator': 'rope.base.builtins.get_iterator', # Python3.3 + } + if pycompat.PY2: + _supported_mapping = dict(( + (k.replace('builtins.', '__builtin__.').replace('_collections_abc.', '_abcoll.'), v) + for k, v in _supported_mapping.items() + )) + + def __call__(self, pyobject, *args, **kwargs): + """ + :type pyobject: rope.base.pyobjects.PyObject + :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None + """ + type_factory = self._get_type_factory(pyobject) + if type_factory: + parametrized_type = type_factory(*args, **kwargs) + if parametrized_type: + return parametrized_type + return pyobject + + def _get_type_factory(self, pyobject): + type_str = '{0}.{1}'.format( + pyobject.get_module().get_name(), + pyobject.get_name(), + ) + if type_str in self._supported_mapping: + return base_utils.resolve(self._supported_mapping[type_str]) + +parametrize_type = ParametrizeType() diff --git a/venv/Lib/site-packages/rope/base/prefs.py b/venv/Lib/site-packages/rope/base/prefs.py new file mode 100644 index 0000000000000000000000000000000000000000..2ab45dac541d9ef4150b81dcc65b7af08db770ca --- /dev/null +++ b/venv/Lib/site-packages/rope/base/prefs.py @@ -0,0 +1,41 @@ +class Prefs(object): + + def __init__(self): + self.prefs = {} + self.callbacks = {} + + def set(self, key, value): + """Set the value of `key` preference to `value`.""" + if key in self.callbacks: + self.callbacks[key](value) + else: + self.prefs[key] = value + + def add(self, key, value): + """Add an entry to a list preference + + Add `value` to the list of entries for the `key` preference. + + """ + if not key in self.prefs: + self.prefs[key] = [] + self.prefs[key].append(value) + + def get(self, key, default=None): + """Get the value of the key preference""" + return self.prefs.get(key, default) + + def add_callback(self, key, callback): + """Add `key` preference with `callback` function + + Whenever `key` is set the callback is called with the + given `value` as parameter. + + """ + self.callbacks[key] = callback + + def __setitem__(self, key, value): + self.set(key, value) + + def __getitem__(self, key): + return self.get(key) diff --git a/venv/Lib/site-packages/rope/base/project.py b/venv/Lib/site-packages/rope/base/project.py new file mode 100644 index 0000000000000000000000000000000000000000..2502b3ea2f447548d97dd09a35a1e3e85f04e485 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/project.py @@ -0,0 +1,491 @@ +import os +import shutil +import sys +import warnings + +import rope.base.fscommands +import rope.base.resourceobserver as resourceobserver +import rope.base.utils.pycompat as pycompat +from rope.base import exceptions, taskhandle, prefs, history, pycore, utils +from rope.base.exceptions import ModuleNotFoundError +from rope.base.resources import File, Folder, _ResourceMatcher + +try: + import cPickle as pickle +except ImportError: + import pickle + + +class _Project(object): + + def __init__(self, fscommands): + self.observers = [] + self.fscommands = fscommands + self.prefs = prefs.Prefs() + self.data_files = _DataFiles(self) + self._custom_source_folders = [] + + def get_resource(self, resource_name): + """Get a resource in a project. + + `resource_name` is the path of a resource in a project. It is + the path of a resource relative to project root. Project root + folder address is an empty string. If the resource does not + exist a `exceptions.ResourceNotFound` exception would be + raised. Use `get_file()` and `get_folder()` when you need to + get nonexistent `Resource`\s. + + """ + path = self._get_resource_path(resource_name) + if not os.path.exists(path): + raise exceptions.ResourceNotFoundError( + 'Resource <%s> does not exist' % resource_name) + elif os.path.isfile(path): + return File(self, resource_name) + elif os.path.isdir(path): + return Folder(self, resource_name) + else: + raise exceptions.ResourceNotFoundError('Unknown resource ' + + resource_name) + + def get_module(self, name, folder=None): + """Returns a `PyObject` if the module was found.""" + # check if this is a builtin module + pymod = self.pycore.builtin_module(name) + if pymod is not None: + return pymod + module = self.find_module(name, folder) + if module is None: + raise ModuleNotFoundError('Module %s not found' % name) + return self.pycore.resource_to_pyobject(module) + + def get_python_path_folders(self): + result = [] + for src in self.prefs.get('python_path', []) + sys.path: + try: + src_folder = get_no_project().get_resource(src) + result.append(src_folder) + except exceptions.ResourceNotFoundError: + pass + return result + + # INFO: It was decided not to cache source folders, since: + # - Does not take much time when the root folder contains + # packages, that is most of the time + # - We need a separate resource observer; `self.observer` + # does not get notified about module and folder creations + def get_source_folders(self): + """Returns project source folders""" + if self.root is None: + return [] + result = list(self._custom_source_folders) + result.extend(self.pycore._find_source_folders(self.root)) + return result + + def validate(self, folder): + """Validate files and folders contained in this folder + + It validates all of the files and folders contained in this + folder if some observers are interested in them. + + """ + for observer in list(self.observers): + observer.validate(folder) + + def add_observer(self, observer): + """Register a `ResourceObserver` + + See `FilteredResourceObserver`. + """ + self.observers.append(observer) + + def remove_observer(self, observer): + """Remove a registered `ResourceObserver`""" + if observer in self.observers: + self.observers.remove(observer) + + def do(self, changes, task_handle=taskhandle.NullTaskHandle()): + """Apply the changes in a `ChangeSet` + + Most of the time you call this function for committing the + changes for a refactoring. + """ + self.history.do(changes, task_handle=task_handle) + + def get_pymodule(self, resource, force_errors=False): + return self.pycore.resource_to_pyobject(resource, force_errors) + + def get_pycore(self): + return self.pycore + + def get_file(self, path): + """Get the file with `path` (it may not exist)""" + return File(self, path) + + def get_folder(self, path): + """Get the folder with `path` (it may not exist)""" + return Folder(self, path) + + def get_prefs(self): + return self.prefs + + def get_relative_module(self, name, folder, level): + module = self.find_relative_module(name, folder, level) + if module is None: + raise ModuleNotFoundError('Module %s not found' % name) + return self.pycore.resource_to_pyobject(module) + + def find_module(self, modname, folder=None): + """Returns a resource corresponding to the given module + + returns None if it can not be found + """ + for src in self.get_source_folders(): + module = _find_module_in_folder(src, modname) + if module is not None: + return module + for src in self.get_python_path_folders(): + module = _find_module_in_folder(src, modname) + if module is not None: + return module + if folder is not None: + module = _find_module_in_folder(folder, modname) + if module is not None: + return module + return None + + def find_relative_module(self, modname, folder, level): + for i in range(level - 1): + folder = folder.parent + if modname == '': + return folder + else: + return _find_module_in_folder(folder, modname) + + def is_ignored(self, resource): + return False + + def _get_resource_path(self, name): + pass + + @property + @utils.saveit + def history(self): + return history.History(self) + + @property + @utils.saveit + def pycore(self): + return pycore.PyCore(self) + + def close(self): + warnings.warn('Cannot close a NoProject', + DeprecationWarning, stacklevel=2) + + ropefolder = None + + +class Project(_Project): + """A Project containing files and folders""" + + def __init__(self, projectroot, fscommands=None, + ropefolder='.ropeproject', **prefs): + """A rope project + + :parameters: + - `projectroot`: The address of the root folder of the project + - `fscommands`: Implements the file system operations used + by rope; have a look at `rope.base.fscommands` + - `ropefolder`: The name of the folder in which rope stores + project configurations and data. Pass `None` for not using + such a folder at all. + - `prefs`: Specify project preferences. These values + overwrite config file preferences. + + """ + if projectroot != '/': + projectroot = _realpath(projectroot).rstrip('/\\') + self._address = projectroot + self._ropefolder_name = ropefolder + if not os.path.exists(self._address): + os.mkdir(self._address) + elif not os.path.isdir(self._address): + raise exceptions.RopeError('Project root exists and' + ' is not a directory') + if fscommands is None: + fscommands = rope.base.fscommands.create_fscommands(self._address) + super(Project, self).__init__(fscommands) + self.ignored = _ResourceMatcher() + self.file_list = _FileListCacher(self) + self.prefs.add_callback('ignored_resources', self.ignored.set_patterns) + if ropefolder is not None: + self.prefs['ignored_resources'] = [ropefolder] + self._init_prefs(prefs) + self._init_source_folders() + + @utils.deprecated('Delete once deprecated functions are gone') + def _init_source_folders(self): + for path in self.prefs.get('source_folders', []): + folder = self.get_resource(path) + self._custom_source_folders.append(folder) + + def get_files(self): + return self.file_list.get_files() + + def get_python_files(self): + """Returns all python files available in the project""" + return [resource for resource in self.get_files() + if self.pycore.is_python_file(resource)] + + def _get_resource_path(self, name): + return os.path.join(self._address, *name.split('/')) + + def _init_ropefolder(self): + if self.ropefolder is not None: + if not self.ropefolder.exists(): + self._create_recursively(self.ropefolder) + if not self.ropefolder.has_child('config.py'): + config = self.ropefolder.create_file('config.py') + config.write(self._default_config()) + + def _create_recursively(self, folder): + if folder.parent != self.root and not folder.parent.exists(): + self._create_recursively(folder.parent) + folder.create() + + def _init_prefs(self, prefs): + run_globals = {} + if self.ropefolder is not None: + config = self.get_file(self.ropefolder.path + '/config.py') + run_globals.update({'__name__': '__main__', + '__builtins__': __builtins__, + '__file__': config.real_path}) + if config.exists(): + config = self.ropefolder.get_child('config.py') + pycompat.execfile(config.real_path, run_globals) + else: + exec(self._default_config(), run_globals) + if 'set_prefs' in run_globals: + run_globals['set_prefs'](self.prefs) + for key, value in prefs.items(): + self.prefs[key] = value + self._init_other_parts() + self._init_ropefolder() + if 'project_opened' in run_globals: + run_globals['project_opened'](self) + + def _default_config(self): + import rope.base.default_config + import inspect + return inspect.getsource(rope.base.default_config) + + def _init_other_parts(self): + # Forcing the creation of `self.pycore` to register observers + self.pycore + + def is_ignored(self, resource): + return self.ignored.does_match(resource) + + def sync(self): + """Closes project open resources""" + self.close() + + def close(self): + """Closes project open resources""" + self.data_files.write() + + def set(self, key, value): + """Set the `key` preference to `value`""" + self.prefs.set(key, value) + + @property + def ropefolder(self): + if self._ropefolder_name is not None: + return self.get_folder(self._ropefolder_name) + + def validate(self, folder=None): + if folder is None: + folder = self.root + super(Project, self).validate(folder) + + root = property(lambda self: self.get_resource('')) + address = property(lambda self: self._address) + + +class NoProject(_Project): + """A null object for holding out of project files. + + This class is singleton use `get_no_project` global function + """ + + def __init__(self): + fscommands = rope.base.fscommands.FileSystemCommands() + super(NoProject, self).__init__(fscommands) + + def _get_resource_path(self, name): + real_name = name.replace('/', os.path.sep) + return _realpath(real_name) + + def get_resource(self, name): + universal_name = _realpath(name).replace(os.path.sep, '/') + return super(NoProject, self).get_resource(universal_name) + + def get_files(self): + return [] + + def get_python_files(self): + return [] + + _no_project = None + + +def get_no_project(): + if NoProject._no_project is None: + NoProject._no_project = NoProject() + return NoProject._no_project + + +class _FileListCacher(object): + + def __init__(self, project): + self.project = project + self.files = None + rawobserver = resourceobserver.ResourceObserver( + self._changed, self._invalid, self._invalid, + self._invalid, self._invalid) + self.project.add_observer(rawobserver) + + def get_files(self): + if self.files is None: + self.files = set() + self._add_files(self.project.root) + return self.files + + def _add_files(self, folder): + for child in folder.get_children(): + if child.is_folder(): + self._add_files(child) + elif not self.project.is_ignored(child): + self.files.add(child) + + def _changed(self, resource): + if resource.is_folder(): + self.files = None + + def _invalid(self, resource, new_resource=None): + self.files = None + + +class _DataFiles(object): + + def __init__(self, project): + self.project = project + self.hooks = [] + + def read_data(self, name, compress=False, import_=False): + if self.project.ropefolder is None: + return None + compress = compress and self._can_compress() + opener = self._get_opener(compress) + file = self._get_file(name, compress) + if not compress and import_: + self._import_old_files(name) + if file.exists(): + input = opener(file.real_path, 'rb') + try: + result = [] + try: + while True: + result.append(pickle.load(input)) + except EOFError: + pass + if len(result) == 1: + return result[0] + if len(result) > 1: + return result + finally: + input.close() + + def write_data(self, name, data, compress=False): + if self.project.ropefolder is not None: + compress = compress and self._can_compress() + file = self._get_file(name, compress) + opener = self._get_opener(compress) + output = opener(file.real_path, 'wb') + try: + pickle.dump(data, output, 2) + finally: + output.close() + + def add_write_hook(self, hook): + self.hooks.append(hook) + + def write(self): + for hook in self.hooks: + hook() + + def _can_compress(self): + try: + import gzip # noqa + return True + except ImportError: + return False + + def _import_old_files(self, name): + old = self._get_file(name + '.pickle', False) + new = self._get_file(name, False) + if old.exists() and not new.exists(): + shutil.move(old.real_path, new.real_path) + + def _get_opener(self, compress): + if compress: + try: + import gzip + return gzip.open + except ImportError: + pass + return open + + def _get_file(self, name, compress): + path = self.project.ropefolder.path + '/' + name + if compress: + path += '.gz' + return self.project.get_file(path) + + +def _realpath(path): + """Return the real path of `path` + + Is equivalent to ``realpath(abspath(expanduser(path)))``. + + Of the particular notice is the hack dealing with the unfortunate + sitaution of running native-Windows python (os.name == 'nt') inside + of Cygwin (abspath starts with '/'), which apparently normal + os.path.realpath completely messes up. + + """ + # there is a bug in cygwin for os.path.abspath() for abs paths + if sys.platform == 'cygwin': + if path[1:3] == ':\\': + return path + elif path[1:3] == ':/': + path = "/cygdrive/" + path[0] + path[2:] + return os.path.abspath(os.path.expanduser(path)) + return os.path.realpath(os.path.abspath(os.path.expanduser(path))) + + +def _find_module_in_folder(folder, modname): + module = folder + packages = modname.split('.') + for pkg in packages[:-1]: + if module.is_folder() and module.has_child(pkg): + module = module.get_child(pkg) + else: + return None + if module.is_folder(): + if module.has_child(packages[-1]) and \ + module.get_child(packages[-1]).is_folder(): + return module.get_child(packages[-1]) + elif module.has_child(packages[-1] + '.py') and \ + not module.get_child(packages[-1] + '.py').is_folder(): + return module.get_child(packages[-1] + '.py') diff --git a/venv/Lib/site-packages/rope/base/pycore.py b/venv/Lib/site-packages/rope/base/pycore.py new file mode 100644 index 0000000000000000000000000000000000000000..c4c1195a4d7ef5ee471655a4e5d97bb2b5836d3d --- /dev/null +++ b/venv/Lib/site-packages/rope/base/pycore.py @@ -0,0 +1,346 @@ +import bisect +import difflib +import sys +import warnings + +import rope.base.libutils +import rope.base.resourceobserver +import rope.base.resources +import rope.base.oi.doa +import rope.base.oi.objectinfo +import rope.base.oi.soa +from rope.base import builtins +from rope.base import exceptions +from rope.base import stdmods +from rope.base import taskhandle +from rope.base import utils +from rope.base.exceptions import ModuleNotFoundError +from rope.base.pyobjectsdef import PyModule, PyPackage + + +class PyCore(object): + + def __init__(self, project): + self.project = project + self._init_resource_observer() + self.cache_observers = [] + self.module_cache = _ModuleCache(self) + self.extension_cache = _ExtensionCache(self) + self.object_info = rope.base.oi.objectinfo.ObjectInfoManager(project) + self._init_python_files() + self._init_automatic_soa() + + def _init_python_files(self): + self.python_matcher = None + patterns = self.project.prefs.get('python_files', None) + if patterns is not None: + self.python_matcher = rope.base.resources._ResourceMatcher() + self.python_matcher.set_patterns(patterns) + + def _init_resource_observer(self): + callback = self._invalidate_resource_cache + observer = rope.base.resourceobserver.ResourceObserver( + changed=callback, moved=callback, removed=callback) + self.observer = \ + rope.base.resourceobserver.FilteredResourceObserver(observer) + self.project.add_observer(self.observer) + + def _init_automatic_soa(self): + if not self.automatic_soa: + return + callback = self._file_changed_for_soa + observer = rope.base.resourceobserver.ResourceObserver( + changed=callback, moved=callback, removed=callback) + self.project.add_observer(observer) + + @property + def automatic_soa(self): + auto_soa = self.project.prefs.get('automatic_soi', None) + return self.project.prefs.get('automatic_soa', auto_soa) + + def _file_changed_for_soa(self, resource, new_resource=None): + old_contents = self.project.history.\ + contents_before_current_change(resource) + if old_contents is not None: + perform_soa_on_changed_scopes(self.project, resource, old_contents) + + def is_python_file(self, resource): + if resource.is_folder(): + return False + if self.python_matcher is None: + return resource.name.endswith('.py') + return self.python_matcher.does_match(resource) + + @utils.deprecated('Use `project.get_module` instead') + def get_module(self, name, folder=None): + """Returns a `PyObject` if the module was found.""" + return self.project.get_module(name, folder) + + def _builtin_submodules(self, modname): + result = {} + for extension in self.extension_modules: + if extension.startswith(modname + '.'): + name = extension[len(modname) + 1:] + if '.' not in name: + result[name] = self.builtin_module(extension) + return result + + def builtin_module(self, name): + return self.extension_cache.get_pymodule(name) + + @utils.deprecated('Use `project.get_relative_module` instead') + def get_relative_module(self, name, folder, level): + return self.project.get_relative_module(name, folder, level) + + @utils.deprecated('Use `libutils.get_string_module` instead') + def get_string_module(self, code, resource=None, force_errors=False): + """Returns a `PyObject` object for the given code + + If `force_errors` is `True`, `exceptions.ModuleSyntaxError` is + raised if module has syntax errors. This overrides + ``ignore_syntax_errors`` project config. + + """ + return PyModule(self, code, resource, force_errors=force_errors) + + @utils.deprecated('Use `libutils.get_string_scope` instead') + def get_string_scope(self, code, resource=None): + """Returns a `Scope` object for the given code""" + return rope.base.libutils.get_string_scope(code, resource) + + def _invalidate_resource_cache(self, resource, new_resource=None): + for observer in self.cache_observers: + observer(resource) + + @utils.deprecated('Use `project.get_python_path_folders` instead') + def get_python_path_folders(self): + return self.project.get_python_path_folders() + + @utils.deprecated('Use `project.find_module` instead') + def find_module(self, modname, folder=None): + """Returns a resource corresponding to the given module + + returns None if it can not be found + """ + return self.project.find_module(modname, folder) + + @utils.deprecated('Use `project.find_relative_module` instead') + def find_relative_module(self, modname, folder, level): + return self.project.find_relative_module(modname, folder, level) + + # INFO: It was decided not to cache source folders, since: + # - Does not take much time when the root folder contains + # packages, that is most of the time + # - We need a separate resource observer; `self.observer` + # does not get notified about module and folder creations + @utils.deprecated('Use `project.get_source_folders` instead') + def get_source_folders(self): + """Returns project source folders""" + return self.project.get_source_folders() + + def resource_to_pyobject(self, resource, force_errors=False): + return self.module_cache.get_pymodule(resource, force_errors) + + @utils.deprecated('Use `project.get_python_files` instead') + def get_python_files(self): + """Returns all python files available in the project""" + return self.project.get_python_files() + + def _is_package(self, folder): + if folder.has_child('__init__.py') and \ + not folder.get_child('__init__.py').is_folder(): + return True + else: + return False + + def _find_source_folders(self, folder): + for resource in folder.get_folders(): + if self._is_package(resource): + return [folder] + result = [] + for resource in folder.get_files(): + if resource.name.endswith('.py'): + result.append(folder) + break + for resource in folder.get_folders(): + result.extend(self._find_source_folders(resource)) + return result + + def run_module(self, resource, args=None, stdin=None, stdout=None): + """Run `resource` module + + Returns a `rope.base.oi.doa.PythonFileRunner` object for + controlling the process. + + """ + perform_doa = self.project.prefs.get('perform_doi', True) + perform_doa = self.project.prefs.get('perform_doa', perform_doa) + receiver = self.object_info.doa_data_received + if not perform_doa: + receiver = None + runner = rope.base.oi.doa.PythonFileRunner( + self, resource, args, stdin, stdout, receiver) + runner.add_finishing_observer(self.module_cache.forget_all_data) + runner.run() + return runner + + def analyze_module(self, resource, should_analyze=lambda py: True, + search_subscopes=lambda py: True, followed_calls=None): + """Analyze `resource` module for static object inference + + This function forces rope to analyze this module to collect + information about function calls. `should_analyze` is a + function that is called with a `PyDefinedObject` argument. If + it returns `True` the element is analyzed. If it is `None` or + returns `False` the element is not analyzed. + + `search_subscopes` is like `should_analyze`; The difference is + that if it returns `False` the sub-scopes are all ignored. + That is it is assumed that `should_analyze` returns `False` + for all of its subscopes. + + `followed_calls` override the value of ``soa_followed_calls`` + project config. + """ + if followed_calls is None: + followed_calls = self.project.prefs.get('soa_followed_calls', 0) + pymodule = self.resource_to_pyobject(resource) + self.module_cache.forget_all_data() + rope.base.oi.soa.analyze_module( + self, pymodule, should_analyze, search_subscopes, followed_calls) + + def get_classes(self, task_handle=taskhandle.NullTaskHandle()): + warnings.warn('`PyCore.get_classes()` is deprecated', + DeprecationWarning, stacklevel=2) + return [] + + def __str__(self): + return str(self.module_cache) + str(self.object_info) + + @utils.deprecated('Use `libutils.modname` instead') + def modname(self, resource): + return rope.base.libutils.modname(resource) + + @property + @utils.cacheit + def extension_modules(self): + result = set(self.project.prefs.get('extension_modules', [])) + if self.project.prefs.get('import_dynload_stdmods', False): + result.update(stdmods.dynload_modules()) + return result + + +class _ModuleCache(object): + + def __init__(self, pycore): + self.pycore = pycore + self.module_map = {} + self.pycore.cache_observers.append(self._invalidate_resource) + self.observer = self.pycore.observer + + def _invalidate_resource(self, resource): + if resource in self.module_map: + self.forget_all_data() + self.observer.remove_resource(resource) + del self.module_map[resource] + + def get_pymodule(self, resource, force_errors=False): + if resource in self.module_map: + return self.module_map[resource] + if resource.is_folder(): + result = PyPackage(self.pycore, resource, + force_errors=force_errors) + else: + result = PyModule(self.pycore, resource=resource, + force_errors=force_errors) + if result.has_errors: + return result + self.module_map[resource] = result + self.observer.add_resource(resource) + return result + + def forget_all_data(self): + for pymodule in self.module_map.values(): + pymodule._forget_concluded_data() + + def __str__(self): + return 'PyCore caches %d PyModules\n' % len(self.module_map) + + +class _ExtensionCache(object): + + def __init__(self, pycore): + self.pycore = pycore + self.extensions = {} + + def get_pymodule(self, name): + if name == '__builtin__': + return builtins.builtins + allowed = self.pycore.extension_modules + if name not in self.extensions and name in allowed: + self.extensions[name] = builtins.BuiltinModule(name, self.pycore) + return self.extensions.get(name) + + +def perform_soa_on_changed_scopes(project, resource, old_contents): + pycore = project.pycore + if resource.exists() and pycore.is_python_file(resource): + try: + new_contents = resource.read() + # detecting changes in new_contents relative to old_contents + detector = _TextChangeDetector(new_contents, old_contents) + + def search_subscopes(pydefined): + scope = pydefined.get_scope() + return detector.is_changed(scope.get_start(), scope.get_end()) + + def should_analyze(pydefined): + scope = pydefined.get_scope() + start = scope.get_start() + end = scope.get_end() + return detector.consume_changes(start, end) + pycore.analyze_module(resource, should_analyze, search_subscopes) + except exceptions.ModuleSyntaxError: + pass + + +class _TextChangeDetector(object): + + def __init__(self, old, new): + self.old = old + self.new = new + self._set_diffs() + + def _set_diffs(self): + differ = difflib.Differ() + self.lines = [] + lineno = 0 + for line in differ.compare(self.old.splitlines(True), + self.new.splitlines(True)): + if line.startswith(' '): + lineno += 1 + elif line.startswith('-'): + lineno += 1 + self.lines.append(lineno) + + def is_changed(self, start, end): + """Tell whether any of start till end lines have changed + + The end points are inclusive and indices start from 1. + """ + left, right = self._get_changed(start, end) + if left < right: + return True + return False + + def consume_changes(self, start, end): + """Clear the changed status of lines from start till end""" + left, right = self._get_changed(start, end) + if left < right: + del self.lines[left:right] + return left < right + + def _get_changed(self, start, end): + left = bisect.bisect_left(self.lines, start) + right = bisect.bisect_right(self.lines, end) + return left, right diff --git a/venv/Lib/site-packages/rope/base/pynames.py b/venv/Lib/site-packages/rope/base/pynames.py new file mode 100644 index 0000000000000000000000000000000000000000..5d489814a35c8a592156711244ada9ee10484b45 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/pynames.py @@ -0,0 +1,201 @@ +import rope.base.pyobjects +from rope.base import exceptions, utils + + +class PyName(object): + """References to `PyObject`\s inside python programs""" + + def get_object(self): + """Return the `PyObject` object referenced by this `PyName`""" + + def get_definition_location(self): + """Return a (module, lineno) tuple""" + + +class DefinedName(PyName): + + def __init__(self, pyobject): + self.pyobject = pyobject + + def get_object(self): + return self.pyobject + + def get_definition_location(self): + return (self.pyobject.get_module(), self.pyobject.get_ast().lineno) + + +class AssignedName(PyName): + """Only a placeholder""" + + +class UnboundName(PyName): + + def __init__(self, pyobject=None): + self.pyobject = pyobject + if self.pyobject is None: + self.pyobject = rope.base.pyobjects.get_unknown() + + def get_object(self): + return self.pyobject + + def get_definition_location(self): + return (None, None) + + +class AssignmentValue(object): + """An assigned expression""" + + def __init__(self, ast_node, levels=None, evaluation='', + assign_type=False): + """The `level` is `None` for simple assignments and is + a list of numbers for tuple assignments for example in:: + + a, (b, c) = x + + The levels for for `a` is ``[0]``, for `b` is ``[1, 0]`` and for + `c` is ``[1, 1]``. + + """ + self.ast_node = ast_node + if levels is None: + self.levels = [] + else: + self.levels = levels + self.evaluation = evaluation + self.assign_type = assign_type + + def get_lineno(self): + return self.ast_node.lineno + + +class EvaluatedName(PyName): + """A name whose object will be evaluated later""" + + def __init__(self, callback, module=None, lineno=None): + self.module = module + self.lineno = lineno + self.callback = callback + self.pyobject = _Inferred(callback, _get_concluded_data(module)) + + def get_object(self): + return self.pyobject.get() + + def get_definition_location(self): + return (self.module, self.lineno) + + def invalidate(self): + """Forget the `PyObject` this `PyName` holds""" + self.pyobject.set(None) + + +class ParameterName(PyName): + """Only a placeholder""" + + +class ImportedModule(PyName): + + def __init__(self, importing_module, module_name=None, + level=0, resource=None): + self.importing_module = importing_module + self.module_name = module_name + self.level = level + self.resource = resource + self.pymodule = _get_concluded_data(self.importing_module) + + def _current_folder(self): + resource = self.importing_module.get_module().get_resource() + if resource is None: + return None + return resource.parent + + def _get_pymodule(self): + if self.pymodule.get() is None: + pycore = self.importing_module.pycore + if self.resource is not None: + self.pymodule.set(pycore.project.get_pymodule(self.resource)) + elif self.module_name is not None: + try: + if self.level == 0: + pymodule = pycore.project.get_module( + self.module_name, self._current_folder()) + else: + pymodule = pycore.project.get_relative_module( + self.module_name, self._current_folder(), + self.level) + self.pymodule.set(pymodule) + except exceptions.ModuleNotFoundError: + pass + return self.pymodule.get() + + def get_object(self): + if self._get_pymodule() is None: + return rope.base.pyobjects.get_unknown() + return self._get_pymodule() + + def get_definition_location(self): + pymodule = self._get_pymodule() + if not isinstance(pymodule, rope.base.pyobjects.PyDefinedObject): + return (None, None) + return (pymodule.get_module(), 1) + + +class ImportedName(PyName): + + def __init__(self, imported_module, imported_name): + self.imported_module = imported_module + self.imported_name = imported_name + + def _get_imported_pyname(self): + try: + result = self.imported_module.get_object()[self.imported_name] + if result != self: + return result + except exceptions.AttributeNotFoundError: + pass + return UnboundName() + + @utils.prevent_recursion(rope.base.pyobjects.get_unknown) + def get_object(self): + return self._get_imported_pyname().get_object() + + @utils.prevent_recursion(lambda: (None, None)) + def get_definition_location(self): + return self._get_imported_pyname().get_definition_location() + + +def _get_concluded_data(module): + if module is None: + return rope.base.pyobjects._ConcludedData() + return module._get_concluded_data() + + +def _circular_inference(): + raise rope.base.pyobjects.IsBeingInferredError( + 'Circular Object Inference') + + +class _Inferred(object): + + def __init__(self, get_inferred, concluded=None): + self.get_inferred = get_inferred + self.concluded = concluded + if self.concluded is None: + self.temp = None + + @utils.prevent_recursion(_circular_inference) + def get(self, *args, **kwds): + if self.concluded is None or self.concluded.get() is None: + self.set(self.get_inferred(*args, **kwds)) + if self._get() is None: + self.set(rope.base.pyobjects.get_unknown()) + return self._get() + + def set(self, pyobject): + if self.concluded is not None: + self.concluded.set(pyobject) + self.temp = pyobject + + def _get(self): + if self.concluded is not None: + return self.concluded.get() + return self.temp diff --git a/venv/Lib/site-packages/rope/base/pynamesdef.py b/venv/Lib/site-packages/rope/base/pynamesdef.py new file mode 100644 index 0000000000000000000000000000000000000000..6dba0a803762d9feb78644b048e2fc06a05c9e16 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/pynamesdef.py @@ -0,0 +1,55 @@ +import rope.base.oi.soi +from rope.base import pynames +from rope.base.pynames import * + + +class AssignedName(pynames.AssignedName): + + def __init__(self, lineno=None, module=None, pyobject=None): + self.lineno = lineno + self.module = module + self.assignments = [] + self.pyobject = _Inferred(self._get_inferred, + pynames._get_concluded_data(module)) + self.pyobject.set(pyobject) + + @utils.prevent_recursion(lambda: None) + def _get_inferred(self): + if self.module is not None: + return rope.base.oi.soi.infer_assigned_object(self) + + def get_object(self): + return self.pyobject.get() + + def get_definition_location(self): + """Returns a (module, lineno) tuple""" + if self.lineno is None and self.assignments: + self.lineno = self.assignments[0].get_lineno() + return (self.module, self.lineno) + + def invalidate(self): + """Forget the `PyObject` this `PyName` holds""" + self.pyobject.set(None) + + +class ParameterName(pynames.ParameterName): + + def __init__(self, pyfunction, index): + self.pyfunction = pyfunction + self.index = index + + def get_object(self): + result = self.pyfunction.get_parameter(self.index) + if result is None: + result = rope.base.pyobjects.get_unknown() + return result + + def get_objects(self): + """Returns the list of objects passed as this parameter""" + return rope.base.oi.soi.get_passed_objects( + self.pyfunction, self.index) + + def get_definition_location(self): + return (self.pyfunction.get_module(), self.pyfunction.get_ast().lineno) + +_Inferred = pynames._Inferred diff --git a/venv/Lib/site-packages/rope/base/pyobjects.py b/venv/Lib/site-packages/rope/base/pyobjects.py new file mode 100644 index 0000000000000000000000000000000000000000..fd4d1c822245146f3c6fe0495e82a148ab0e8009 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/pyobjects.py @@ -0,0 +1,311 @@ +from rope.base.fscommands import _decode_data +from rope.base import ast, exceptions, utils + + +class PyObject(object): + + def __init__(self, type_): + if type_ is None: + type_ = self + self.type = type_ + + def get_attributes(self): + if self.type is self: + return {} + return self.type.get_attributes() + + def get_attribute(self, name): + if name not in self.get_attributes(): + raise exceptions.AttributeNotFoundError( + 'Attribute %s not found' % name) + return self.get_attributes()[name] + + def get_type(self): + return self.type + + def __getitem__(self, key): + """The same as ``get_attribute(key)``""" + return self.get_attribute(key) + + def __contains__(self, key): + """The same as ``key in self.get_attributes()``""" + return key in self.get_attributes() + + def __eq__(self, obj): + """Check the equality of two `PyObject`\s + + Currently it is assumed that instances (the direct instances + of `PyObject`, not the instances of its subclasses) are equal + if their types are equal. For every other object like + defineds or builtins rope assumes objects are reference + objects and their identities should match. + + """ + if self.__class__ != obj.__class__: + return False + if type(self) == PyObject: + if self is not self.type: + return self.type == obj.type + else: + return self.type is obj.type + return self is obj + + def __ne__(self, obj): + return not self.__eq__(obj) + + def __hash__(self): + """See docs for `__eq__()` method""" + if type(self) == PyObject and self != self.type: + return hash(self.type) + 1 + else: + return super(PyObject, self).__hash__() + + def __iter__(self): + """The same as ``iter(self.get_attributes())``""" + return iter(self.get_attributes()) + + _types = None + _unknown = None + + @staticmethod + def _get_base_type(name): + if PyObject._types is None: + PyObject._types = {} + base_type = PyObject(None) + PyObject._types['Type'] = base_type + PyObject._types['Module'] = PyObject(base_type) + PyObject._types['Function'] = PyObject(base_type) + PyObject._types['Unknown'] = PyObject(base_type) + return PyObject._types[name] + + +def get_base_type(name): + """Return the base type with name `name`. + + The base types are 'Type', 'Function', 'Module' and 'Unknown'. It + was used to check the type of a `PyObject` but currently its use + is discouraged. Use classes defined in this module instead. + For example instead of + ``pyobject.get_type() == get_base_type('Function')`` use + ``isinstance(pyobject, AbstractFunction)``. + + You can use `AbstractClass` for classes, `AbstractFunction` for + functions, and `AbstractModule` for modules. You can also use + `PyFunction` and `PyClass` for testing if an object is + defined somewhere and rope can access its source. These classes + provide more methods. + + """ + return PyObject._get_base_type(name) + + +def get_unknown(): + """Return a pyobject whose type is unknown + + Note that two unknown objects are equal. So for example you can + write:: + + if pyname.get_object() == get_unknown(): + print('cannot determine what this pyname holds') + + Rope could have used `None` for indicating unknown objects but + we had to check that in many places. So actually this method + returns a null object. + + """ + if PyObject._unknown is None: + PyObject._unknown = PyObject(get_base_type('Unknown')) + return PyObject._unknown + + +class AbstractClass(PyObject): + + def __init__(self): + super(AbstractClass, self).__init__(get_base_type('Type')) + + def get_name(self): + pass + + def get_doc(self): + pass + + def get_superclasses(self): + return [] + + +class AbstractFunction(PyObject): + + def __init__(self): + super(AbstractFunction, self).__init__(get_base_type('Function')) + + def get_name(self): + pass + + def get_doc(self): + pass + + def get_param_names(self, special_args=True): + return [] + + def get_returned_object(self, args): + return get_unknown() + + +class AbstractModule(PyObject): + + def __init__(self, doc=None): + super(AbstractModule, self).__init__(get_base_type('Module')) + + def get_doc(self): + pass + + def get_resource(self): + pass + + +class PyDefinedObject(object): + """Python defined names that rope can access their sources""" + + def __init__(self, pycore, ast_node, parent): + self.pycore = pycore + self.ast_node = ast_node + self.scope = None + self.parent = parent + self.structural_attributes = None + self.concluded_attributes = self.get_module()._get_concluded_data() + self.attributes = self.get_module()._get_concluded_data() + self.defineds = None + + visitor_class = None + + @utils.prevent_recursion(lambda: {}) + def _get_structural_attributes(self): + if self.structural_attributes is None: + self.structural_attributes = self._create_structural_attributes() + return self.structural_attributes + + @utils.prevent_recursion(lambda: {}) + def _get_concluded_attributes(self): + if self.concluded_attributes.get() is None: + self._get_structural_attributes() + self.concluded_attributes.set(self._create_concluded_attributes()) + return self.concluded_attributes.get() + + def get_attributes(self): + if self.attributes.get() is None: + result = dict(self._get_concluded_attributes()) + result.update(self._get_structural_attributes()) + self.attributes.set(result) + return self.attributes.get() + + def get_attribute(self, name): + if name in self._get_structural_attributes(): + return self._get_structural_attributes()[name] + if name in self._get_concluded_attributes(): + return self._get_concluded_attributes()[name] + raise exceptions.AttributeNotFoundError('Attribute %s not found' % + name) + + def get_scope(self): + if self.scope is None: + self.scope = self._create_scope() + return self.scope + + def get_module(self): + current_object = self + while current_object.parent is not None: + current_object = current_object.parent + return current_object + + def get_doc(self): + if len(self.get_ast().body) > 0: + expr = self.get_ast().body[0] + if isinstance(expr, ast.Expr) and \ + isinstance(expr.value, ast.Str): + docstring = expr.value.s + coding = self.get_module().coding + return _decode_data(docstring, coding) + + def _get_defined_objects(self): + if self.defineds is None: + self._get_structural_attributes() + return self.defineds + + def _create_structural_attributes(self): + if self.visitor_class is None: + return {} + new_visitor = self.visitor_class(self.pycore, self) + for child in ast.get_child_nodes(self.ast_node): + ast.walk(child, new_visitor) + self.defineds = new_visitor.defineds + return new_visitor.names + + def _create_concluded_attributes(self): + return {} + + def get_ast(self): + return self.ast_node + + def _create_scope(self): + pass + + +class PyFunction(PyDefinedObject, AbstractFunction): + """Only a placeholder""" + + +class PyClass(PyDefinedObject, AbstractClass): + """Only a placeholder""" + + +class _ConcludedData(object): + + def __init__(self): + self.data_ = None + + def set(self, data): + self.data_ = data + + def get(self): + return self.data_ + + data = property(get, set) + + def _invalidate(self): + self.data = None + + def __str__(self): + return '<' + str(self.data) + '>' + + +class _PyModule(PyDefinedObject, AbstractModule): + + def __init__(self, pycore, ast_node, resource): + self.resource = resource + self.concluded_data = [] + AbstractModule.__init__(self) + PyDefinedObject.__init__(self, pycore, ast_node, None) + + def _get_concluded_data(self): + new_data = _ConcludedData() + self.concluded_data.append(new_data) + return new_data + + def _forget_concluded_data(self): + for data in self.concluded_data: + data._invalidate() + + def get_resource(self): + return self.resource + + +class PyModule(_PyModule): + """Only a placeholder""" + + +class PyPackage(_PyModule): + """Only a placeholder""" + + +class IsBeingInferredError(exceptions.RopeError): + pass diff --git a/venv/Lib/site-packages/rope/base/pyobjectsdef.py b/venv/Lib/site-packages/rope/base/pyobjectsdef.py new file mode 100644 index 0000000000000000000000000000000000000000..44fbeb3c232083afc9fc1abed550db5fd5278842 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/pyobjectsdef.py @@ -0,0 +1,571 @@ +import rope.base.builtins +import rope.base.codeanalyze +import rope.base.evaluate +import rope.base.libutils +import rope.base.oi.soi +import rope.base.pyscopes +from rope.base import (pynamesdef as pynames, exceptions, ast, + astutils, pyobjects, fscommands, arguments, utils) +from rope.base.utils import pycompat + +try: + unicode +except NameError: + unicode = str + + +class PyFunction(pyobjects.PyFunction): + + def __init__(self, pycore, ast_node, parent): + rope.base.pyobjects.AbstractFunction.__init__(self) + rope.base.pyobjects.PyDefinedObject.__init__( + self, pycore, ast_node, parent) + self.arguments = self.ast_node.args + self.parameter_pyobjects = pynames._Inferred( + self._infer_parameters, self.get_module()._get_concluded_data()) + self.returned = pynames._Inferred(self._infer_returned) + self.parameter_pynames = None + + def _create_structural_attributes(self): + return {} + + def _create_concluded_attributes(self): + return {} + + def _create_scope(self): + return rope.base.pyscopes.FunctionScope(self.pycore, self, + _FunctionVisitor) + + def _infer_parameters(self): + pyobjects = rope.base.oi.soi.infer_parameter_objects(self) + self._handle_special_args(pyobjects) + return pyobjects + + def _infer_returned(self, args=None): + return rope.base.oi.soi.infer_returned_object(self, args) + + def _handle_special_args(self, pyobjects): + if len(pyobjects) == len(self.arguments.args): + if self.arguments.vararg: + pyobjects.append(rope.base.builtins.get_list()) + if self.arguments.kwarg: + pyobjects.append(rope.base.builtins.get_dict()) + + def _set_parameter_pyobjects(self, pyobjects): + if pyobjects is not None: + self._handle_special_args(pyobjects) + self.parameter_pyobjects.set(pyobjects) + + def get_parameters(self): + if self.parameter_pynames is None: + result = {} + for index, name in enumerate(self.get_param_names()): + # TODO: handle tuple parameters + result[name] = pynames.ParameterName(self, index) + self.parameter_pynames = result + return self.parameter_pynames + + def get_parameter(self, index): + if index < len(self.parameter_pyobjects.get()): + return self.parameter_pyobjects.get()[index] + + def get_returned_object(self, args): + return self.returned.get(args) + + def get_name(self): + return self.get_ast().name + + def get_param_names(self, special_args=True): + # TODO: handle tuple parameters + result = [pycompat.get_ast_arg_arg(node) for node in self.arguments.args + if isinstance(node, pycompat.ast_arg_type)] + if special_args: + if self.arguments.vararg: + result.append(pycompat.get_ast_arg_arg(self.arguments.vararg)) + if self.arguments.kwarg: + result.append(pycompat.get_ast_arg_arg(self.arguments.kwarg)) + return result + + def get_kind(self): + """Get function type + + It returns one of 'function', 'method', 'staticmethod' or + 'classmethod' strs. + + """ + scope = self.parent.get_scope() + if isinstance(self.parent, PyClass): + for decorator in self.decorators: + pyname = rope.base.evaluate.eval_node(scope, decorator) + if pyname == rope.base.builtins.builtins['staticmethod']: + return 'staticmethod' + if pyname == rope.base.builtins.builtins['classmethod']: + return 'classmethod' + return 'method' + return 'function' + + @property + def decorators(self): + try: + return getattr(self.ast_node, 'decorator_list') + except AttributeError: + return getattr(self.ast_node, 'decorators', None) + + +class PyClass(pyobjects.PyClass): + + def __init__(self, pycore, ast_node, parent): + self.visitor_class = _ClassVisitor + rope.base.pyobjects.AbstractClass.__init__(self) + rope.base.pyobjects.PyDefinedObject.__init__( + self, pycore, ast_node, parent) + self.parent = parent + self._superclasses = self.get_module()._get_concluded_data() + + def get_superclasses(self): + if self._superclasses.get() is None: + self._superclasses.set(self._get_bases()) + return self._superclasses.get() + + def get_name(self): + return self.get_ast().name + + def _create_concluded_attributes(self): + result = {} + for base in reversed(self.get_superclasses()): + result.update(base.get_attributes()) + return result + + def _get_bases(self): + result = [] + for base_name in self.ast_node.bases: + base = rope.base.evaluate.eval_node(self.parent.get_scope(), + base_name) + if base is not None and \ + base.get_object().get_type() == \ + rope.base.pyobjects.get_base_type('Type'): + result.append(base.get_object()) + return result + + def _create_scope(self): + return rope.base.pyscopes.ClassScope(self.pycore, self) + + +class PyModule(pyobjects.PyModule): + + def __init__(self, pycore, source=None, + resource=None, force_errors=False): + ignore = pycore.project.prefs.get('ignore_syntax_errors', False) + syntax_errors = force_errors or not ignore + self.has_errors = False + try: + source, node = self._init_source(pycore, source, resource) + except exceptions.ModuleSyntaxError: + self.has_errors = True + if syntax_errors: + raise + else: + source = '\n' + node = ast.parse('\n') + self.source_code = source + self.star_imports = [] + self.visitor_class = _GlobalVisitor + self.coding = fscommands.read_str_coding(self.source_code) + super(PyModule, self).__init__(pycore, node, resource) + + def _init_source(self, pycore, source_code, resource): + filename = 'string' + if resource: + filename = resource.path + try: + if source_code is None: + source_bytes = resource.read_bytes() + source_code = fscommands.file_data_to_unicode(source_bytes) + else: + if isinstance(source_code, unicode): + source_bytes = fscommands.unicode_to_file_data(source_code) + else: + source_bytes = source_code + ast_node = ast.parse(source_bytes, filename=filename) + except SyntaxError as e: + raise exceptions.ModuleSyntaxError(filename, e.lineno, e.msg) + except UnicodeDecodeError as e: + raise exceptions.ModuleSyntaxError(filename, 1, '%s' % (e.reason)) + return source_code, ast_node + + @utils.prevent_recursion(lambda: {}) + def _create_concluded_attributes(self): + result = {} + for star_import in self.star_imports: + result.update(star_import.get_names()) + return result + + def _create_scope(self): + return rope.base.pyscopes.GlobalScope(self.pycore, self) + + @property + @utils.saveit + def lines(self): + """A `SourceLinesAdapter`""" + return rope.base.codeanalyze.SourceLinesAdapter(self.source_code) + + @property + @utils.saveit + def logical_lines(self): + """A `LogicalLinesFinder`""" + return rope.base.codeanalyze.CachingLogicalLineFinder(self.lines) + + def get_name(self): + return rope.base.libutils.modname(self.get_resource()) + +class PyPackage(pyobjects.PyPackage): + + def __init__(self, pycore, resource=None, force_errors=False): + self.resource = resource + init_dot_py = self._get_init_dot_py() + if init_dot_py is not None: + ast_node = pycore.project.get_pymodule( + init_dot_py, force_errors=force_errors).get_ast() + else: + ast_node = ast.parse('\n') + super(PyPackage, self).__init__(pycore, ast_node, resource) + + def _create_structural_attributes(self): + result = {} + modname = rope.base.libutils.modname(self.resource) + extension_submodules = self.pycore._builtin_submodules(modname) + for name, module in extension_submodules.items(): + result[name] = rope.base.builtins.BuiltinName(module) + if self.resource is None: + return result + for name, resource in self._get_child_resources().items(): + result[name] = pynames.ImportedModule(self, resource=resource) + return result + + def _create_concluded_attributes(self): + result = {} + init_dot_py = self._get_init_dot_py() + if init_dot_py: + init_object = self.pycore.project.get_pymodule(init_dot_py) + result.update(init_object.get_attributes()) + return result + + def _get_child_resources(self): + result = {} + for child in self.resource.get_children(): + if child.is_folder(): + result[child.name] = child + elif child.name.endswith('.py') and \ + child.name != '__init__.py': + name = child.name[:-3] + result[name] = child + return result + + def _get_init_dot_py(self): + if self.resource is not None and \ + self.resource.has_child('__init__.py'): + return self.resource.get_child('__init__.py') + else: + return None + + def _create_scope(self): + return self.get_module().get_scope() + + def get_module(self): + init_dot_py = self._get_init_dot_py() + if init_dot_py: + return self.pycore.project.get_pymodule(init_dot_py) + return self + + +class _AssignVisitor(object): + + def __init__(self, scope_visitor): + self.scope_visitor = scope_visitor + self.assigned_ast = None + + def _Assign(self, node): + self.assigned_ast = node.value + for child_node in node.targets: + ast.walk(child_node, self) + + def _assigned(self, name, assignment=None): + self.scope_visitor._assigned(name, assignment) + + def _Name(self, node): + assignment = None + if self.assigned_ast is not None: + assignment = pynames.AssignmentValue(self.assigned_ast) + self._assigned(node.id, assignment) + + def _Tuple(self, node): + names = astutils.get_name_levels(node) + for name, levels in names: + assignment = None + if self.assigned_ast is not None: + assignment = pynames.AssignmentValue(self.assigned_ast, levels) + self._assigned(name, assignment) + + def _Attribute(self, node): + pass + + def _Subscript(self, node): + pass + + def _Slice(self, node): + pass + + +class _ScopeVisitor(object): + + def __init__(self, pycore, owner_object): + self.pycore = pycore + self.owner_object = owner_object + self.names = {} + self.defineds = [] + + def get_module(self): + if self.owner_object is not None: + return self.owner_object.get_module() + else: + return None + + def _ClassDef(self, node): + pyclass = PyClass(self.pycore, node, self.owner_object) + self.names[node.name] = pynames.DefinedName(pyclass) + self.defineds.append(pyclass) + + def _FunctionDef(self, node): + pyfunction = PyFunction(self.pycore, node, self.owner_object) + for decorator in pyfunction.decorators: + if isinstance(decorator, ast.Name) and decorator.id == 'property': + if isinstance(self, _ClassVisitor): + type_ = rope.base.builtins.Property(pyfunction) + arg = pynames.UnboundName( + rope.base.pyobjects.PyObject(self.owner_object)) + + def _eval(type_=type_, arg=arg): + return type_.get_property_object( + arguments.ObjectArguments([arg])) + self.names[node.name] = pynames.EvaluatedName( + _eval, module=self.get_module(), lineno=node.lineno) + break + else: + self.names[node.name] = pynames.DefinedName(pyfunction) + self.defineds.append(pyfunction) + + def _AsyncFunctionDef(self, node): + return self._FunctionDef(node) + + def _Assign(self, node): + ast.walk(node, _AssignVisitor(self)) + + def _AugAssign(self, node): + pass + + def _For(self, node): + names = self._update_evaluated(node.target, node.iter, # noqa + '.__iter__().next()') + for child in node.body + node.orelse: + ast.walk(child, self) + + def _AsyncFor(self, node): + return self._For(node) + + def _assigned(self, name, assignment): + pyname = self.names.get(name, None) + if pyname is None: + pyname = pynames.AssignedName(module=self.get_module()) + if isinstance(pyname, pynames.AssignedName): + if assignment is not None: + pyname.assignments.append(assignment) + self.names[name] = pyname + + def _update_evaluated(self, targets, assigned, + evaluation='', eval_type=False): + result = {} + if isinstance(targets, str): + assignment = pynames.AssignmentValue(assigned, [], + evaluation, eval_type) + self._assigned(targets, assignment) + else: + names = astutils.get_name_levels(targets) + for name, levels in names: + assignment = pynames.AssignmentValue(assigned, levels, + evaluation, eval_type) + self._assigned(name, assignment) + return result + + def _With(self, node): + for item in pycompat.get_ast_with_items(node): + if item.optional_vars: + self._update_evaluated(item.optional_vars, + item.context_expr, '.__enter__()') + for child in node.body: + ast.walk(child, self) + + def _AsyncWith(self, node): + return self._With(node) + + def _excepthandler(self, node): + node_name_type = str if pycompat.PY3 else ast.Name + if node.name is not None and isinstance(node.name, node_name_type): + type_node = node.type + if isinstance(node.type, ast.Tuple) and type_node.elts: + type_node = type_node.elts[0] + self._update_evaluated(node.name, type_node, eval_type=True) + + for child in node.body: + ast.walk(child, self) + + def _ExceptHandler(self, node): + self._excepthandler(node) + + def _Import(self, node): + for import_pair in node.names: + module_name = import_pair.name + alias = import_pair.asname + first_package = module_name.split('.')[0] + if alias is not None: + imported = pynames.ImportedModule(self.get_module(), + module_name) + if not self._is_ignored_import(imported): + self.names[alias] = imported + else: + imported = pynames.ImportedModule(self.get_module(), + first_package) + if not self._is_ignored_import(imported): + self.names[first_package] = imported + + def _ImportFrom(self, node): + level = 0 + if node.level: + level = node.level + imported_module = pynames.ImportedModule(self.get_module(), + node.module, level) + if self._is_ignored_import(imported_module): + return + if len(node.names) == 1 and node.names[0].name == '*': + if isinstance(self.owner_object, PyModule): + self.owner_object.star_imports.append( + StarImport(imported_module)) + else: + for imported_name in node.names: + imported = imported_name.name + alias = imported_name.asname + if alias is not None: + imported = alias + self.names[imported] = pynames.ImportedName(imported_module, + imported_name.name) + + def _is_ignored_import(self, imported_module): + if not self.pycore.project.prefs.get('ignore_bad_imports', False): + return False + return not isinstance(imported_module.get_object(), + rope.base.pyobjects.AbstractModule) + + def _Global(self, node): + module = self.get_module() + for name in node.names: + if module is not None: + try: + pyname = module[name] + except exceptions.AttributeNotFoundError: + pyname = pynames.AssignedName(node.lineno) + self.names[name] = pyname + + +class _GlobalVisitor(_ScopeVisitor): + + def __init__(self, pycore, owner_object): + super(_GlobalVisitor, self).__init__(pycore, owner_object) + + +class _ClassVisitor(_ScopeVisitor): + + def __init__(self, pycore, owner_object): + super(_ClassVisitor, self).__init__(pycore, owner_object) + + def _FunctionDef(self, node): + _ScopeVisitor._FunctionDef(self, node) + if len(node.args.args) > 0: + first = node.args.args[0] + new_visitor = None + if isinstance(first, pycompat.ast_arg_type): + new_visitor = _ClassInitVisitor(self, pycompat.get_ast_arg_arg(first)) + if new_visitor is not None: + for child in ast.get_child_nodes(node): + ast.walk(child, new_visitor) + + +class _FunctionVisitor(_ScopeVisitor): + + def __init__(self, pycore, owner_object): + super(_FunctionVisitor, self).__init__(pycore, owner_object) + self.returned_asts = [] + self.generator = False + + def _Return(self, node): + if node.value is not None: + self.returned_asts.append(node.value) + + def _Yield(self, node): + if node.value is not None: + self.returned_asts.append(node.value) + self.generator = True + + +class _ClassInitVisitor(_AssignVisitor): + + def __init__(self, scope_visitor, self_name): + super(_ClassInitVisitor, self).__init__(scope_visitor) + self.self_name = self_name + + def _Attribute(self, node): + if not isinstance(node.ctx, ast.Store): + return + if isinstance(node.value, ast.Name) and \ + node.value.id == self.self_name: + if node.attr not in self.scope_visitor.names: + self.scope_visitor.names[node.attr] = pynames.AssignedName( + lineno=node.lineno, module=self.scope_visitor.get_module()) + if self.assigned_ast is not None: + pyname = self.scope_visitor.names[node.attr] + if isinstance(pyname, pynames.AssignedName): + pyname.assignments.append( + pynames.AssignmentValue(self.assigned_ast)) + + def _Tuple(self, node): + if not isinstance(node.ctx, ast.Store): + return + for child in ast.get_child_nodes(node): + ast.walk(child, self) + + def _Name(self, node): + pass + + def _FunctionDef(self, node): + pass + + def _ClassDef(self, node): + pass + + def _For(self, node): + pass + + def _With(self, node): + pass + + +class StarImport(object): + + def __init__(self, imported_module): + self.imported_module = imported_module + + def get_names(self): + result = {} + imported = self.imported_module.get_object() + for name in imported: + if not name.startswith('_'): + result[name] = pynames.ImportedName(self.imported_module, name) + return result diff --git a/venv/Lib/site-packages/rope/base/pyscopes.py b/venv/Lib/site-packages/rope/base/pyscopes.py new file mode 100644 index 0000000000000000000000000000000000000000..0bed19a92912ccda603e3f470ca48154c417bb59 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/pyscopes.py @@ -0,0 +1,314 @@ +import rope.base.builtins +import rope.base.codeanalyze +import rope.base.pynames +from rope.base import ast, exceptions, utils + + +class Scope(object): + + def __init__(self, pycore, pyobject, parent_scope): + self.pycore = pycore + self.pyobject = pyobject + self.parent = parent_scope + + def get_names(self): + """Return the names defined or imported in this scope""" + return self.pyobject.get_attributes() + + def get_defined_names(self): + """Return the names defined in this scope""" + return self.pyobject._get_structural_attributes() + + def get_name(self, name): + """Return name `PyName` defined in this scope""" + if name not in self.get_names(): + raise exceptions.NameNotFoundError('name %s not found' % name) + return self.get_names()[name] + + def __getitem__(self, key): + """The same as ``get_name(key)``""" + return self.get_name(key) + + def __contains__(self, key): + """The same as ``key in self.get_names()``""" + return key in self.get_names() + + @utils.saveit + def get_scopes(self): + """Return the subscopes of this scope + + The returned scopes should be sorted by the order they appear. + """ + return self._create_scopes() + + def lookup(self, name): + if name in self.get_names(): + return self.get_names()[name] + if self.parent is not None: + return self.parent._propagated_lookup(name) + return None + + def get_propagated_names(self): + """Return the visible names of this scope + + Return the names defined in this scope that are visible from + scopes containing this scope. This method returns the same + dictionary returned by `get_names()` except for `ClassScope` + which returns an empty dict. + """ + return self.get_names() + + def _propagated_lookup(self, name): + if name in self.get_propagated_names(): + return self.get_propagated_names()[name] + if self.parent is not None: + return self.parent._propagated_lookup(name) + return None + + def _create_scopes(self): + return [pydefined.get_scope() + for pydefined in self.pyobject._get_defined_objects()] + + def _get_global_scope(self): + current = self + while current.parent is not None: + current = current.parent + return current + + def get_start(self): + return self.pyobject.get_ast().lineno + + def get_body_start(self): + body = self.pyobject.get_ast().body + if body: + return body[0].lineno + return self.get_start() + + def get_end(self): + pymodule = self._get_global_scope().pyobject + return pymodule.logical_lines.logical_line_in(self.logical_end)[1] + + @utils.saveit + def get_logical_end(self): + global_scope = self._get_global_scope() + return global_scope._scope_finder.find_scope_end(self) + + start = property(get_start) + end = property(get_end) + logical_end = property(get_logical_end) + + def get_kind(self): + pass + + +class GlobalScope(Scope): + + def __init__(self, pycore, module): + super(GlobalScope, self).__init__(pycore, module, None) + self.names = module._get_concluded_data() + + def get_start(self): + return 1 + + def get_kind(self): + return 'Module' + + def get_name(self, name): + try: + return self.pyobject[name] + except exceptions.AttributeNotFoundError: + if name in self.builtin_names: + return self.builtin_names[name] + raise exceptions.NameNotFoundError('name %s not found' % name) + + def get_names(self): + if self.names.get() is None: + result = dict(self.builtin_names) + result.update(super(GlobalScope, self).get_names()) + self.names.set(result) + return self.names.get() + + def get_inner_scope_for_line(self, lineno, indents=None): + return self._scope_finder.get_holding_scope(self, lineno, indents) + + def get_inner_scope_for_offset(self, offset): + return self._scope_finder.get_holding_scope_for_offset(self, offset) + + @property + @utils.saveit + def _scope_finder(self): + return _HoldingScopeFinder(self.pyobject) + + @property + def builtin_names(self): + return rope.base.builtins.builtins.get_attributes() + + +class FunctionScope(Scope): + + def __init__(self, pycore, pyobject, visitor): + super(FunctionScope, self).__init__(pycore, pyobject, + pyobject.parent.get_scope()) + self.names = None + self.returned_asts = None + self.is_generator = None + self.defineds = None + self.visitor = visitor + + def _get_names(self): + if self.names is None: + self._visit_function() + return self.names + + def _visit_function(self): + if self.names is None: + new_visitor = self.visitor(self.pycore, self.pyobject) + for n in ast.get_child_nodes(self.pyobject.get_ast()): + ast.walk(n, new_visitor) + self.names = new_visitor.names + self.names.update(self.pyobject.get_parameters()) + self.returned_asts = new_visitor.returned_asts + self.is_generator = new_visitor.generator + self.defineds = new_visitor.defineds + + def _get_returned_asts(self): + if self.names is None: + self._visit_function() + return self.returned_asts + + def _is_generator(self): + if self.is_generator is None: + self._get_returned_asts() + return self.is_generator + + def get_names(self): + return self._get_names() + + def _create_scopes(self): + if self.defineds is None: + self._visit_function() + return [pydefined.get_scope() for pydefined in self.defineds] + + def get_kind(self): + return 'Function' + + def invalidate_data(self): + for pyname in self.get_names().values(): + if isinstance(pyname, (rope.base.pynames.AssignedName, + rope.base.pynames.EvaluatedName)): + pyname.invalidate() + + +class ClassScope(Scope): + + def __init__(self, pycore, pyobject): + super(ClassScope, self).__init__(pycore, pyobject, + pyobject.parent.get_scope()) + + def get_kind(self): + return 'Class' + + def get_propagated_names(self): + return {} + + +class _HoldingScopeFinder(object): + + def __init__(self, pymodule): + self.pymodule = pymodule + + def get_indents(self, lineno): + return rope.base.codeanalyze.count_line_indents( + self.lines.get_line(lineno)) + + def _get_scope_indents(self, scope): + return self.get_indents(scope.get_start()) + + def get_holding_scope(self, module_scope, lineno, line_indents=None): + if line_indents is None: + line_indents = self.get_indents(lineno) + current_scope = module_scope + new_scope = current_scope + while new_scope is not None and \ + (new_scope.get_kind() == 'Module' or + self._get_scope_indents(new_scope) <= line_indents): + current_scope = new_scope + if current_scope.get_start() == lineno and \ + current_scope.get_kind() != 'Module': + return current_scope + new_scope = None + for scope in current_scope.get_scopes(): + if scope.get_start() <= lineno: + if lineno <= scope.get_end(): + new_scope = scope + break + else: + break + return current_scope + + def _is_empty_line(self, lineno): + line = self.lines.get_line(lineno) + return line.strip() == '' or line.lstrip().startswith('#') + + def _get_body_indents(self, scope): + return self.get_indents(scope.get_body_start()) + + def get_holding_scope_for_offset(self, scope, offset): + return self.get_holding_scope( + scope, self.lines.get_line_number(offset)) + + def find_scope_end(self, scope): + if not scope.parent: + return self.lines.length() + end = scope.pyobject.get_ast().body[-1].lineno + scope_start = self.pymodule.logical_lines.logical_line_in(scope.start) + if scope_start[1] >= end: + # handling one-liners + body_indents = self._get_scope_indents(scope) + 4 + else: + body_indents = self._get_body_indents(scope) + for l in self.logical_lines.generate_starts( + min(end + 1, self.lines.length()), self.lines.length() + 1): + if not self._is_empty_line(l): + if self.get_indents(l) < body_indents: + return end + else: + end = l + return end + + @property + def lines(self): + return self.pymodule.lines + + @property + def code(self): + return self.pymodule.source_code + + @property + def logical_lines(self): + return self.pymodule.logical_lines + + +class TemporaryScope(Scope): + """Currently used for list comprehensions and generator expressions + + These scopes do not appear in the `get_scopes()` method of their + parent scopes. + """ + + def __init__(self, pycore, parent_scope, names): + super(TemporaryScope, self).__init__( + pycore, parent_scope.pyobject, parent_scope) + self.names = names + + def get_names(self): + return self.names + + def get_defined_names(self): + return self.names + + def _create_scopes(self): + return [] + + def get_kind(self): + return 'Temporary' diff --git a/venv/Lib/site-packages/rope/base/resourceobserver.py b/venv/Lib/site-packages/rope/base/resourceobserver.py new file mode 100644 index 0000000000000000000000000000000000000000..7c0937d5bbacb4f73a8eb59ba7e8141e072a8dcd --- /dev/null +++ b/venv/Lib/site-packages/rope/base/resourceobserver.py @@ -0,0 +1,272 @@ +import os + + +class ResourceObserver(object): + """Provides the interface for observing resources + + `ResourceObserver`\s can be registered using `Project. + add_observer()`. But most of the time `FilteredResourceObserver` + should be used. `ResourceObserver`\s report all changes passed + to them and they don't report changes to all resources. For + example if a folder is removed, it only calls `removed()` for that + folder and not its contents. You can use + `FilteredResourceObserver` if you are interested in changes only + to a list of resources. And you want changes to be reported on + individual resources. + + """ + + def __init__(self, changed=None, moved=None, created=None, + removed=None, validate=None): + self.changed = changed + self.moved = moved + self.created = created + self.removed = removed + self._validate = validate + + def resource_changed(self, resource): + """It is called when the resource changes""" + if self.changed is not None: + self.changed(resource) + + def resource_moved(self, resource, new_resource): + """It is called when a resource is moved""" + if self.moved is not None: + self.moved(resource, new_resource) + + def resource_created(self, resource): + """Is called when a new resource is created""" + if self.created is not None: + self.created(resource) + + def resource_removed(self, resource): + """Is called when a new resource is removed""" + if self.removed is not None: + self.removed(resource) + + def validate(self, resource): + """Validate the existence of this resource and its children. + + This function is called when rope need to update its resource + cache about the files that might have been changed or removed + by other processes. + + """ + if self._validate is not None: + self._validate(resource) + + +class FilteredResourceObserver(object): + """A useful decorator for `ResourceObserver` + + Most resource observers have a list of resources and are + interested only in changes to those files. This class satisfies + this need. It dispatches resource changed and removed messages. + It performs these tasks: + + * Changes to files and folders are analyzed to check whether any + of the interesting resources are changed or not. If they are, + it reports these changes to `resource_observer` passed to the + constructor. + * When a resource is removed it checks whether any of the + interesting resources are contained in that folder and reports + them to `resource_observer`. + * When validating a folder it validates all of the interesting + files in that folder. + + Since most resource observers are interested in a list of + resources that change over time, `add_resource` and + `remove_resource` might be useful. + + """ + + def __init__(self, resource_observer, initial_resources=None, + timekeeper=None): + self.observer = resource_observer + self.resources = {} + if timekeeper is not None: + self.timekeeper = timekeeper + else: + self.timekeeper = ChangeIndicator() + if initial_resources is not None: + for resource in initial_resources: + self.add_resource(resource) + + def add_resource(self, resource): + """Add a resource to the list of interesting resources""" + if resource.exists(): + self.resources[resource] = self.timekeeper.get_indicator(resource) + else: + self.resources[resource] = None + + def remove_resource(self, resource): + """Add a resource to the list of interesting resources""" + if resource in self.resources: + del self.resources[resource] + + def clear_resources(self): + """Removes all registered resources""" + self.resources.clear() + + def resource_changed(self, resource): + changes = _Changes() + self._update_changes_caused_by_changed(changes, resource) + self._perform_changes(changes) + + def _update_changes_caused_by_changed(self, changes, changed): + if changed in self.resources: + changes.add_changed(changed) + if self._is_parent_changed(changed): + changes.add_changed(changed.parent) + + def _update_changes_caused_by_moved(self, changes, resource, + new_resource=None): + if resource in self.resources: + changes.add_removed(resource, new_resource) + if new_resource in self.resources: + changes.add_created(new_resource) + if resource.is_folder(): + for file in list(self.resources): + if resource.contains(file): + new_file = self._calculate_new_resource( + resource, new_resource, file) + changes.add_removed(file, new_file) + if self._is_parent_changed(resource): + changes.add_changed(resource.parent) + if new_resource is not None: + if self._is_parent_changed(new_resource): + changes.add_changed(new_resource.parent) + + def _is_parent_changed(self, child): + return child.parent in self.resources + + def resource_moved(self, resource, new_resource): + changes = _Changes() + self._update_changes_caused_by_moved(changes, resource, new_resource) + self._perform_changes(changes) + + def resource_created(self, resource): + changes = _Changes() + self._update_changes_caused_by_created(changes, resource) + self._perform_changes(changes) + + def _update_changes_caused_by_created(self, changes, resource): + if resource in self.resources: + changes.add_created(resource) + if self._is_parent_changed(resource): + changes.add_changed(resource.parent) + + def resource_removed(self, resource): + changes = _Changes() + self._update_changes_caused_by_moved(changes, resource) + self._perform_changes(changes) + + def _perform_changes(self, changes): + for resource in changes.changes: + self.observer.resource_changed(resource) + self.resources[resource] = self.timekeeper.get_indicator(resource) + for resource, new_resource in changes.moves.items(): + self.resources[resource] = None + if new_resource is not None: + self.observer.resource_moved(resource, new_resource) + else: + self.observer.resource_removed(resource) + for resource in changes.creations: + self.observer.resource_created(resource) + self.resources[resource] = self.timekeeper.get_indicator(resource) + + def validate(self, resource): + changes = _Changes() + for file in self._search_resource_moves(resource): + if file in self.resources: + self._update_changes_caused_by_moved(changes, file) + for file in self._search_resource_changes(resource): + if file in self.resources: + self._update_changes_caused_by_changed(changes, file) + for file in self._search_resource_creations(resource): + if file in self.resources: + changes.add_created(file) + self._perform_changes(changes) + + def _search_resource_creations(self, resource): + creations = set() + if resource in self.resources and resource.exists() and \ + self.resources[resource] is None: + creations.add(resource) + if resource.is_folder(): + for file in self.resources: + if file.exists() and resource.contains(file) and \ + self.resources[file] is None: + creations.add(file) + return creations + + def _search_resource_moves(self, resource): + all_moved = set() + if resource in self.resources and not resource.exists(): + all_moved.add(resource) + if resource.is_folder(): + for file in self.resources: + if resource.contains(file): + if not file.exists(): + all_moved.add(file) + moved = set(all_moved) + for folder in [file for file in all_moved if file.is_folder()]: + if folder in moved: + for file in list(moved): + if folder.contains(file): + moved.remove(file) + return moved + + def _search_resource_changes(self, resource): + changed = set() + if resource in self.resources and self._is_changed(resource): + changed.add(resource) + if resource.is_folder(): + for file in self.resources: + if file.exists() and resource.contains(file): + if self._is_changed(file): + changed.add(file) + return changed + + def _is_changed(self, resource): + if self.resources[resource] is None: + return False + return self.resources[resource] != \ + self.timekeeper.get_indicator(resource) + + def _calculate_new_resource(self, main, new_main, resource): + if new_main is None: + return None + diff = resource.path[len(main.path):] + return resource.project.get_resource(new_main.path + diff) + + +class ChangeIndicator(object): + + def get_indicator(self, resource): + """Return the modification time and size of a `Resource`.""" + path = resource.real_path + # on dos, mtime does not change for a folder when files are added + if os.name != 'posix' and os.path.isdir(path): + return (os.path.getmtime(path), + len(os.listdir(path)), + os.path.getsize(path)) + return (os.path.getmtime(path), + os.path.getsize(path)) + + +class _Changes(object): + + def __init__(self): + self.changes = set() + self.creations = set() + self.moves = {} + + def add_changed(self, resource): + self.changes.add(resource) + + def add_removed(self, resource, new_resource=None): + self.moves[resource] = new_resource + + def add_created(self, resource): + self.creations.add(resource) diff --git a/venv/Lib/site-packages/rope/base/resources.py b/venv/Lib/site-packages/rope/base/resources.py new file mode 100644 index 0000000000000000000000000000000000000000..4f02779c3cfdd4a06d0b77ca2a0b7f464398cb10 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/resources.py @@ -0,0 +1,247 @@ +"""Files and folders in a project are represented as resource objects. + +Files and folders are access through `Resource` objects. `Resource` has +two subclasses: `File` and `Folder`. What we care about is that +refactorings and `rope.base.change.Change`s use resources. + +There are two options to create a `Resource` for a path in a project. +Note that in these examples `path` is the path to a file or folder +relative to the project's root. A project's root folder is represented +by an empty string. + + 1) Use the `rope.base.Project.get_resource()` method. E.g.: + + myresource = myproject.get_resource(path) + + + 2) Use the `rope.base.libutils` module. `libutils` has a function + named `path_to_resource()`. It takes a project and a path: + + from rope.base import libutils + + myresource = libutils.path_to_resource(myproject, path) + +Once we have a `Resource`, we can retrieve information from it, like +getting the path relative to the project's root (via `path`), reading +from and writing to the resource, moving the resource, etc. +""" + +import os +import re + +from rope.base import change +from rope.base import exceptions +from rope.base import fscommands + + +class Resource(object): + """Represents files and folders in a project""" + + def __init__(self, project, path): + self.project = project + self._path = path + + def move(self, new_location): + """Move resource to `new_location`""" + self._perform_change(change.MoveResource(self, new_location), + 'Moving <%s> to <%s>' % (self.path, new_location)) + + def remove(self): + """Remove resource from the project""" + self._perform_change(change.RemoveResource(self), + 'Removing <%s>' % self.path) + + def is_folder(self): + """Return true if the resource is a folder""" + + def create(self): + """Create this resource""" + + def exists(self): + return os.path.exists(self.real_path) + + @property + def parent(self): + parent = '/'.join(self.path.split('/')[0:-1]) + return self.project.get_folder(parent) + + @property + def path(self): + """Return the path of this resource relative to the project root + + The path is the list of parent directories separated by '/' followed + by the resource name. + """ + return self._path + + @property + def name(self): + """Return the name of this resource""" + return self.path.split('/')[-1] + + @property + def real_path(self): + """Return the file system path of this resource""" + return self.project._get_resource_path(self.path) + + def __eq__(self, obj): + return self.__class__ == obj.__class__ and self.path == obj.path + + def __ne__(self, obj): + return not self.__eq__(obj) + + def __hash__(self): + return hash(self.path) + + def _perform_change(self, change_, description): + changes = change.ChangeSet(description) + changes.add_change(change_) + self.project.do(changes) + + +class File(Resource): + """Represents a file""" + + def __init__(self, project, name): + super(File, self).__init__(project, name) + + def read(self): + data = self.read_bytes() + try: + return fscommands.file_data_to_unicode(data) + except UnicodeDecodeError as e: + raise exceptions.ModuleDecodeError(self.path, e.reason) + + def read_bytes(self): + handle = open(self.real_path, 'rb') + try: + return handle.read() + finally: + handle.close() + + def write(self, contents): + try: + if contents == self.read(): + return + except IOError: + pass + self._perform_change(change.ChangeContents(self, contents), + 'Writing file <%s>' % self.path) + + def is_folder(self): + return False + + def create(self): + self.parent.create_file(self.name) + + +class Folder(Resource): + """Represents a folder""" + + def __init__(self, project, name): + super(Folder, self).__init__(project, name) + + def is_folder(self): + return True + + def get_children(self): + """Return the children of this folder""" + try: + children = os.listdir(self.real_path) + except OSError: + return [] + result = [] + for name in children: + try: + child = self.get_child(name) + except exceptions.ResourceNotFoundError: + continue + if not self.project.is_ignored(child): + result.append(self.get_child(name)) + return result + + def create_file(self, file_name): + self._perform_change( + change.CreateFile(self, file_name), + 'Creating file <%s>' % self._get_child_path(file_name)) + return self.get_child(file_name) + + def create_folder(self, folder_name): + self._perform_change( + change.CreateFolder(self, folder_name), + 'Creating folder <%s>' % self._get_child_path(folder_name)) + return self.get_child(folder_name) + + def _get_child_path(self, name): + if self.path: + return self.path + '/' + name + else: + return name + + def get_child(self, name): + return self.project.get_resource(self._get_child_path(name)) + + def has_child(self, name): + try: + self.get_child(name) + return True + except exceptions.ResourceNotFoundError: + return False + + def get_files(self): + return [resource for resource in self.get_children() + if not resource.is_folder()] + + def get_folders(self): + return [resource for resource in self.get_children() + if resource.is_folder()] + + def contains(self, resource): + if self == resource: + return False + return self.path == '' or resource.path.startswith(self.path + '/') + + def create(self): + self.parent.create_folder(self.name) + + +class _ResourceMatcher(object): + + def __init__(self): + self.patterns = [] + self._compiled_patterns = [] + + def set_patterns(self, patterns): + """Specify which resources to match + + `patterns` is a `list` of `str`\s that can contain ``*`` and + ``?`` signs for matching resource names. + + """ + self._compiled_patterns = None + self.patterns = patterns + + def _add_pattern(self, pattern): + re_pattern = pattern.replace('.', '\\.').\ + replace('*', '[^/]*').replace('?', '[^/]').\ + replace('//', '/(.*/)?') + re_pattern = '^(.*/)?' + re_pattern + '(/.*)?$' + self.compiled_patterns.append(re.compile(re_pattern)) + + def does_match(self, resource): + for pattern in self.compiled_patterns: + if pattern.match(resource.path): + return True + path = os.path.join(resource.project.address, + *resource.path.split('/')) + if os.path.islink(path): + return True + return False + + @property + def compiled_patterns(self): + if self._compiled_patterns is None: + self._compiled_patterns = [] + for pattern in self.patterns: + self._add_pattern(pattern) + return self._compiled_patterns diff --git a/venv/Lib/site-packages/rope/base/simplify.py b/venv/Lib/site-packages/rope/base/simplify.py new file mode 100644 index 0000000000000000000000000000000000000000..bc4cade4ab910989e3143c17df1180a2770794b9 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/simplify.py @@ -0,0 +1,55 @@ +"""A module to ease code analysis + +This module is here to help source code analysis. +""" +import re + +from rope.base import codeanalyze, utils + + +@utils.cached(7) +def real_code(source): + """Simplify `source` for analysis + + It replaces: + + * comments with spaces + * strs with a new str filled with spaces + * implicit and explicit continuations with spaces + * tabs and semicolons with spaces + + The resulting code is a lot easier to analyze if we are interested + only in offsets. + """ + collector = codeanalyze.ChangeCollector(source) + for start, end in ignored_regions(source): + if source[start] == '#': + replacement = ' ' * (end - start) + else: + replacement = '"%s"' % (' ' * (end - start - 2)) + collector.add_change(start, end, replacement) + source = collector.get_changed() or source + collector = codeanalyze.ChangeCollector(source) + parens = 0 + for match in _parens.finditer(source): + i = match.start() + c = match.group() + if c in '({[': + parens += 1 + if c in ')}]': + parens -= 1 + if c == '\n' and parens > 0: + collector.add_change(i, i + 1, ' ') + source = collector.get_changed() or source + return source.replace('\\\n', ' ').replace('\t', ' ').replace(';', '\n') + + +@utils.cached(7) +def ignored_regions(source): + """Return ignored regions like strings and comments in `source` """ + return [(match.start(), match.end()) for match in _str.finditer(source)] + + +_str = re.compile('%s|%s' % (codeanalyze.get_comment_pattern(), + codeanalyze.get_string_pattern())) +_parens = re.compile(r'[\({\[\]}\)\n]') diff --git a/venv/Lib/site-packages/rope/base/stdmods.py b/venv/Lib/site-packages/rope/base/stdmods.py new file mode 100644 index 0000000000000000000000000000000000000000..7d5db1d10ba79250960165a20b50c7c450c6da08 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/stdmods.py @@ -0,0 +1,65 @@ +import os +import re +import sys + +from rope.base import utils +from rope.base.utils import pycompat + + +def _stdlib_path(): + if pycompat.PY2: + from distutils import sysconfig + return sysconfig.get_python_lib(standard_lib=True, + plat_specific=True) + elif pycompat.PY3: + import inspect + return os.path.dirname(inspect.getsourcefile(inspect)) + + +@utils.cached(1) +def standard_modules(): + return python_modules() | dynload_modules() + + +@utils.cached(1) +def python_modules(): + result = set() + lib_path = _stdlib_path() + if os.path.exists(lib_path): + for name in os.listdir(lib_path): + path = os.path.join(lib_path, name) + if os.path.isdir(path): + if '-' not in name: + result.add(name) + else: + if name.endswith('.py'): + result.add(name[:-3]) + return result + + +def normalize_so_name(name): + """ + Handle different types of python installations + """ + if "cpython" in name: + return os.path.splitext(os.path.splitext(name)[0])[0] + # XXX: Special handling for Fedora python2 distribution + # See: https://github.com/python-rope/rope/issues/211 + if name == "timemodule.so": + return "time" + return os.path.splitext(name)[0] + + +@utils.cached(1) +def dynload_modules(): + result = set(sys.builtin_module_names) + dynload_path = os.path.join(_stdlib_path(), 'lib-dynload') + if os.path.exists(dynload_path): + for name in os.listdir(dynload_path): + path = os.path.join(dynload_path, name) + if os.path.isfile(path): + if name.endswith('.dll'): + result.add(normalize_so_name(name)) + if name.endswith('.so'): + result.add(normalize_so_name(name)) + return result diff --git a/venv/Lib/site-packages/rope/base/taskhandle.py b/venv/Lib/site-packages/rope/base/taskhandle.py new file mode 100644 index 0000000000000000000000000000000000000000..c1f01b984399454ca6ad6a095e83905904035a0e --- /dev/null +++ b/venv/Lib/site-packages/rope/base/taskhandle.py @@ -0,0 +1,131 @@ +from rope.base import exceptions + + +class TaskHandle(object): + + def __init__(self, name='Task', interrupts=True): + """Construct a TaskHandle + + If `interrupts` is `False` the task won't be interrupted by + calling `TaskHandle.stop()`. + + """ + self.name = name + self.interrupts = interrupts + self.stopped = False + self.job_sets = [] + self.observers = [] + + def stop(self): + """Interrupts the refactoring""" + if self.interrupts: + self.stopped = True + self._inform_observers() + + def current_jobset(self): + """Return the current `JobSet`""" + if self.job_sets: + return self.job_sets[-1] + + def add_observer(self, observer): + """Register an observer for this task handle + + The observer is notified whenever the task is stopped or + a job gets finished. + + """ + self.observers.append(observer) + + def is_stopped(self): + return self.stopped + + def get_jobsets(self): + return self.job_sets + + def create_jobset(self, name='JobSet', count=None): + result = JobSet(self, name=name, count=count) + self.job_sets.append(result) + self._inform_observers() + return result + + def _inform_observers(self): + for observer in list(self.observers): + observer() + + +class JobSet(object): + + def __init__(self, handle, name, count): + self.handle = handle + self.name = name + self.count = count + self.done = 0 + self.job_name = None + + def started_job(self, name): + self.check_status() + self.job_name = name + self.handle._inform_observers() + + def finished_job(self): + self.check_status() + self.done += 1 + self.handle._inform_observers() + self.job_name = None + + def check_status(self): + if self.handle.is_stopped(): + raise exceptions.InterruptedTaskError() + + def get_active_job_name(self): + return self.job_name + + def get_percent_done(self): + if self.count is not None and self.count > 0: + percent = self.done * 100 // self.count + return min(percent, 100) + + def get_name(self): + return self.name + + +class NullTaskHandle(object): + + def __init__(self): + pass + + def is_stopped(self): + return False + + def stop(self): + pass + + def create_jobset(self, *args, **kwds): + return NullJobSet() + + def get_jobsets(self): + return [] + + def add_observer(self, observer): + pass + + +class NullJobSet(object): + + def started_job(self, name): + pass + + def finished_job(self): + pass + + def check_status(self): + pass + + def get_active_job_name(self): + pass + + def get_percent_done(self): + pass + + def get_name(self): + pass diff --git a/venv/Lib/site-packages/rope/base/utils/__init__.py b/venv/Lib/site-packages/rope/base/utils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0fba8596f180b9468765b76c7d881e5026b33cec --- /dev/null +++ b/venv/Lib/site-packages/rope/base/utils/__init__.py @@ -0,0 +1,98 @@ +import sys +import warnings + + +def saveit(func): + """A decorator that caches the return value of a function""" + + name = '_' + func.__name__ + + def _wrapper(self, *args, **kwds): + if not hasattr(self, name): + setattr(self, name, func(self, *args, **kwds)) + return getattr(self, name) + return _wrapper + +cacheit = saveit + + +def prevent_recursion(default): + """A decorator that returns the return value of `default` in recursions""" + def decorator(func): + name = '_calling_%s_' % func.__name__ + + def newfunc(self, *args, **kwds): + if getattr(self, name, False): + return default() + setattr(self, name, True) + try: + return func(self, *args, **kwds) + finally: + setattr(self, name, False) + return newfunc + return decorator + + +def ignore_exception(exception_class): + """A decorator that ignores `exception_class` exceptions""" + def _decorator(func): + def newfunc(*args, **kwds): + try: + return func(*args, **kwds) + except exception_class: + pass + return newfunc + return _decorator + + +def deprecated(message=None): + """A decorator for deprecated functions""" + def _decorator(func, message=message): + if message is None: + message = '%s is deprecated' % func.__name__ + + def newfunc(*args, **kwds): + warnings.warn(message, DeprecationWarning, stacklevel=2) + return func(*args, **kwds) + return newfunc + return _decorator + + +def cached(size): + """A caching decorator based on parameter objects""" + def decorator(func): + cached_func = _Cached(func, size) + return lambda *a, **kw: cached_func(*a, **kw) + return decorator + + +class _Cached(object): + + def __init__(self, func, count): + self.func = func + self.cache = [] + self.count = count + + def __call__(self, *args, **kwds): + key = (args, kwds) + for cached_key, cached_result in self.cache: + if cached_key == key: + return cached_result + result = self.func(*args, **kwds) + self.cache.append((key, result)) + if len(self.cache) > self.count: + del self.cache[0] + return result + + +def resolve(str_or_obj): + """Returns object from string""" + from rope.base.utils.pycompat import string_types + if not isinstance(str_or_obj, string_types): + return str_or_obj + if '.' not in str_or_obj: + str_or_obj += '.' + mod_name, obj_name = str_or_obj.rsplit('.', 1) + __import__(mod_name) + mod = sys.modules[mod_name] + return getattr(mod, obj_name) if obj_name else mod diff --git a/venv/Lib/site-packages/rope/base/utils/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/rope/base/utils/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..029c533c3e0f46368753aeae139f6f968a1a584d Binary files /dev/null and b/venv/Lib/site-packages/rope/base/utils/__pycache__/__init__.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/utils/__pycache__/datastructures.cpython-37.pyc b/venv/Lib/site-packages/rope/base/utils/__pycache__/datastructures.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..adef60ff49710d1b811cd73edfbfb88c011ee573 Binary files /dev/null and b/venv/Lib/site-packages/rope/base/utils/__pycache__/datastructures.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/utils/__pycache__/pycompat.cpython-37.pyc b/venv/Lib/site-packages/rope/base/utils/__pycache__/pycompat.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9dd514a8dbd0dd98d73b75ed5d321e18dbb31bcc Binary files /dev/null and b/venv/Lib/site-packages/rope/base/utils/__pycache__/pycompat.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/base/utils/datastructures.py b/venv/Lib/site-packages/rope/base/utils/datastructures.py new file mode 100644 index 0000000000000000000000000000000000000000..0cb16cf2b19ed0d2649b00c6b4f3f082bbb79312 --- /dev/null +++ b/venv/Lib/site-packages/rope/base/utils/datastructures.py @@ -0,0 +1,67 @@ +# this snippet was taken from this link +# http://code.activestate.com/recipes/576694/ + +import collections + + +class OrderedSet(collections.MutableSet): + + def __init__(self, iterable=None): + self.end = end = [] + end += [None, end, end] # sentinel + # node for doubly linked list + self.map = {} # key --> [key, prev, next] + if iterable is not None: + self |= iterable + + def __len__(self): + return len(self.map) + + def __contains__(self, key): + return key in self.map + + def add(self, key): + if key not in self.map: + end = self.end + curr = end[1] + curr[2] = end[1] = self.map[key] = [key, curr, end] + + def intersection(self, set_b): + return OrderedSet([item for item in self if item in set_b]) + + def discard(self, key): + if key in self.map: + key, prev, next = self.map.pop(key) + prev[2] = next + next[1] = prev + + def __iter__(self): + end = self.end + curr = end[2] + while curr is not end: + yield curr[0] + curr = curr[2] + + def __reversed__(self): + end = self.end + curr = end[1] + while curr is not end: + yield curr[0] + curr = curr[1] + + def pop(self, last=True): + if not self: + raise KeyError('set is empty') + key = self.end[1][0] if last else self.end[2][0] + self.discard(key) + return key + + def __repr__(self): + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, list(self)) + + def __eq__(self, other): + if isinstance(other, OrderedSet): + return len(self) == len(other) and list(self) == list(other) + return set(self) == set(other) diff --git a/venv/Lib/site-packages/rope/base/utils/pycompat.py b/venv/Lib/site-packages/rope/base/utils/pycompat.py new file mode 100644 index 0000000000000000000000000000000000000000..367cf09228f4630a7381fb0cb3272b779c96dfff --- /dev/null +++ b/venv/Lib/site-packages/rope/base/utils/pycompat.py @@ -0,0 +1,45 @@ +import sys +import _ast +# from rope.base import ast + +PY2 = sys.version_info[0] == 2 +PY27 = sys.version_info[0:2] >= (2, 7) +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +try: + str = unicode +except NameError: # PY3 + + str = str + string_types = (str,) + import builtins + ast_arg_type = _ast.arg + + def execfile(fn, global_vars=None, local_vars=None): + with open(fn) as f: + code = compile(f.read(), fn, 'exec') + exec(code, global_vars or {}, local_vars) + + def get_ast_arg_arg(node): + if isinstance(node, string_types): # TODO: G21: Understand the Algorithm (Where it's used?) + return node + return node.arg + + def get_ast_with_items(node): + return node.items + +else: # PY2 + + string_types = (basestring,) + builtins = __import__('__builtin__') + ast_arg_type = _ast.Name + execfile = execfile + + def get_ast_arg_arg(node): + if isinstance(node, string_types): # Python2 arguments.vararg, arguments.kwarg + return node + return node.id + + def get_ast_with_items(node): + return [node] diff --git a/venv/Lib/site-packages/rope/base/worder.py b/venv/Lib/site-packages/rope/base/worder.py new file mode 100644 index 0000000000000000000000000000000000000000..2436eafe29f113c8a44cf3af81911e982596e0db --- /dev/null +++ b/venv/Lib/site-packages/rope/base/worder.py @@ -0,0 +1,551 @@ +import bisect +import keyword + +import rope.base.simplify + + +def get_name_at(resource, offset): + source_code = resource.read() + word_finder = Worder(source_code) + return word_finder.get_word_at(offset) + + +class Worder(object): + """A class for finding boundaries of words and expressions + + Note that in these methods, offset should be the index of the + character not the index of the character after it. + """ + + def __init__(self, code, handle_ignores=False): + simplified = rope.base.simplify.real_code(code) + self.code_finder = _RealFinder(simplified, code) + self.handle_ignores = handle_ignores + self.code = code + + def _init_ignores(self): + ignores = rope.base.simplify.ignored_regions(self.code) + self.dumb_finder = _RealFinder(self.code, self.code) + self.starts = [ignored[0] for ignored in ignores] + self.ends = [ignored[1] for ignored in ignores] + + def _context_call(self, name, offset): + if self.handle_ignores: + if not hasattr(self, 'starts'): + self._init_ignores() + start = bisect.bisect(self.starts, offset) + if start > 0 and offset < self.ends[start - 1]: + return getattr(self.dumb_finder, name)(offset) + return getattr(self.code_finder, name)(offset) + + def get_primary_at(self, offset): + return self._context_call('get_primary_at', offset) + + def get_word_at(self, offset): + return self._context_call('get_word_at', offset) + + def get_primary_range(self, offset): + return self._context_call('get_primary_range', offset) + + def get_splitted_primary_before(self, offset): + return self._context_call('get_splitted_primary_before', offset) + + def get_word_range(self, offset): + return self._context_call('get_word_range', offset) + + def is_function_keyword_parameter(self, offset): + return self.code_finder.is_function_keyword_parameter(offset) + + def is_a_class_or_function_name_in_header(self, offset): + return self.code_finder.is_a_class_or_function_name_in_header(offset) + + def is_from_statement_module(self, offset): + return self.code_finder.is_from_statement_module(offset) + + def is_from_aliased(self, offset): + return self.code_finder.is_from_aliased(offset) + + def is_import_statement_aliased_module(self, offset): + return self.code_finder.is_import_statement_aliased_module(offset) + + def find_parens_start_from_inside(self, offset): + return self.code_finder.find_parens_start_from_inside(offset) + + def is_a_name_after_from_import(self, offset): + return self.code_finder.is_a_name_after_from_import(offset) + + def is_from_statement(self, offset): + return self.code_finder.is_from_statement(offset) + + def get_from_aliased(self, offset): + return self.code_finder.get_from_aliased(offset) + + def is_import_statement(self, offset): + return self.code_finder.is_import_statement(offset) + + def is_assigned_here(self, offset): + return self.code_finder.is_assigned_here(offset) + + def is_a_function_being_called(self, offset): + return self.code_finder.is_a_function_being_called(offset) + + def get_word_parens_range(self, offset): + return self.code_finder.get_word_parens_range(offset) + + def is_name_assigned_in_class_body(self, offset): + return self.code_finder.is_name_assigned_in_class_body(offset) + + def is_on_function_call_keyword(self, offset): + return self.code_finder.is_on_function_call_keyword(offset) + + def _find_parens_start(self, offset): + return self.code_finder._find_parens_start(offset) + + def get_parameters(self, first, last): + return self.code_finder.get_parameters(first, last) + + def get_from_module(self, offset): + return self.code_finder.get_from_module(offset) + + def is_assigned_in_a_tuple_assignment(self, offset): + return self.code_finder.is_assigned_in_a_tuple_assignment(offset) + + def get_assignment_type(self, offset): + return self.code_finder.get_assignment_type(offset) + + def get_function_and_args_in_header(self, offset): + return self.code_finder.get_function_and_args_in_header(offset) + + def get_lambda_and_args(self, offset): + return self.code_finder.get_lambda_and_args(offset) + + def find_function_offset(self, offset): + return self.code_finder.find_function_offset(offset) + + +class _RealFinder(object): + + def __init__(self, code, raw): + self.code = code + self.raw = raw + + def _find_word_start(self, offset): + current_offset = offset + while current_offset >= 0 and self._is_id_char(current_offset): + current_offset -= 1 + return current_offset + 1 + + def _find_word_end(self, offset): + while offset + 1 < len(self.code) and self._is_id_char(offset + 1): + offset += 1 + return offset + + def _find_last_non_space_char(self, offset): + while offset >= 0 and self.code[offset].isspace(): + if self.code[offset] == '\n': + return offset + offset -= 1 + return max(-1, offset) + + def get_word_at(self, offset): + offset = self._get_fixed_offset(offset) + return self.raw[self._find_word_start(offset): + self._find_word_end(offset) + 1] + + def _get_fixed_offset(self, offset): + if offset >= len(self.code): + return offset - 1 + if not self._is_id_char(offset): + if offset > 0 and self._is_id_char(offset - 1): + return offset - 1 + if offset < len(self.code) - 1 and self._is_id_char(offset + 1): + return offset + 1 + return offset + + def _is_id_char(self, offset): + return self.code[offset].isalnum() or self.code[offset] == '_' + + def _find_string_start(self, offset): + kind = self.code[offset] + try: + return self.code.rindex(kind, 0, offset) + except ValueError: + return 0 + + def _find_parens_start(self, offset): + offset = self._find_last_non_space_char(offset - 1) + while offset >= 0 and self.code[offset] not in '[({': + if self.code[offset] not in ':,': + offset = self._find_primary_start(offset) + offset = self._find_last_non_space_char(offset - 1) + return offset + + def _find_atom_start(self, offset): + old_offset = offset + if self.code[offset] == '\n': + return offset + 1 + if self.code[offset].isspace(): + offset = self._find_last_non_space_char(offset) + if self.code[offset] in '\'"': + return self._find_string_start(offset) + if self.code[offset] in ')]}': + return self._find_parens_start(offset) + if self._is_id_char(offset): + return self._find_word_start(offset) + return old_offset + + def _find_primary_without_dot_start(self, offset): + """It tries to find the undotted primary start + + It is different from `self._get_atom_start()` in that it + follows function calls, too; such as in ``f(x)``. + + """ + last_atom = offset + offset = self._find_last_non_space_char(last_atom) + while offset > 0 and self.code[offset] in ')]': + last_atom = self._find_parens_start(offset) + offset = self._find_last_non_space_char(last_atom - 1) + if offset >= 0 and (self.code[offset] in '"\'})]' or + self._is_id_char(offset)): + atom_start = self._find_atom_start(offset) + if not keyword.iskeyword(self.code[atom_start:offset + 1]): + return atom_start + return last_atom + + def _find_primary_start(self, offset): + if offset >= len(self.code): + offset = len(self.code) - 1 + if self.code[offset] != '.': + offset = self._find_primary_without_dot_start(offset) + else: + offset = offset + 1 + while offset > 0: + prev = self._find_last_non_space_char(offset - 1) + if offset <= 0 or self.code[prev] != '.': + break + offset = self._find_primary_without_dot_start(prev - 1) + if not self._is_id_char(offset): + break + + return offset + + def get_primary_at(self, offset): + offset = self._get_fixed_offset(offset) + start, end = self.get_primary_range(offset) + return self.raw[start:end].strip() + + def get_splitted_primary_before(self, offset): + """returns expression, starting, starting_offset + + This function is used in `rope.codeassist.assist` function. + """ + if offset == 0: + return ('', '', 0) + end = offset - 1 + word_start = self._find_atom_start(end) + real_start = self._find_primary_start(end) + if self.code[word_start:offset].strip() == '': + word_start = end + if self.code[end].isspace(): + word_start = end + if self.code[real_start:word_start].strip() == '': + real_start = word_start + if real_start == word_start == end and not self._is_id_char(end): + return ('', '', offset) + if real_start == word_start: + return ('', self.raw[word_start:offset], word_start) + else: + if self.code[end] == '.': + return (self.raw[real_start:end], '', offset) + last_dot_position = word_start + if self.code[word_start] != '.': + last_dot_position = \ + self._find_last_non_space_char(word_start - 1) + last_char_position = \ + self._find_last_non_space_char(last_dot_position - 1) + if self.code[word_start].isspace(): + word_start = offset + return (self.raw[real_start:last_char_position + 1], + self.raw[word_start:offset], word_start) + + def _get_line_start(self, offset): + try: + return self.code.rindex('\n', 0, offset + 1) + except ValueError: + return 0 + + def _get_line_end(self, offset): + try: + return self.code.index('\n', offset) + except ValueError: + return len(self.code) + + def is_name_assigned_in_class_body(self, offset): + word_start = self._find_word_start(offset - 1) + word_end = self._find_word_end(offset) + 1 + if '.' in self.code[word_start:word_end]: + return False + line_start = self._get_line_start(word_start) + line = self.code[line_start:word_start].strip() + return not line and self.get_assignment_type(offset) == '=' + + def is_a_class_or_function_name_in_header(self, offset): + word_start = self._find_word_start(offset - 1) + line_start = self._get_line_start(word_start) + prev_word = self.code[line_start:word_start].strip() + return prev_word in ['def', 'class'] + + def _find_first_non_space_char(self, offset): + if offset >= len(self.code): + return len(self.code) + while offset < len(self.code) and self.code[offset].isspace(): + if self.code[offset] == '\n': + return offset + offset += 1 + return offset + + def is_a_function_being_called(self, offset): + word_end = self._find_word_end(offset) + 1 + next_char = self._find_first_non_space_char(word_end) + return next_char < len(self.code) and \ + self.code[next_char] == '(' and \ + not self.is_a_class_or_function_name_in_header(offset) + + def _find_import_end(self, start): + return self._get_line_end(start) + + def is_import_statement(self, offset): + try: + last_import = self.code.rindex('import ', 0, offset) + except ValueError: + return False + line_start = self._get_line_start(last_import) + return (self._find_import_end(last_import + 7) >= offset and + self._find_word_start(line_start) == last_import) + + def is_from_statement(self, offset): + try: + last_from = self.code.rindex('from ', 0, offset) + from_import = self.code.index(' import ', last_from) + from_names = from_import + 8 + except ValueError: + return False + from_names = self._find_first_non_space_char(from_names) + return self._find_import_end(from_names) >= offset + + def is_from_statement_module(self, offset): + if offset >= len(self.code) - 1: + return False + stmt_start = self._find_primary_start(offset) + line_start = self._get_line_start(stmt_start) + prev_word = self.code[line_start:stmt_start].strip() + return prev_word == 'from' + + def is_import_statement_aliased_module(self, offset): + if not self.is_import_statement(offset): + return False + try: + line_start = self._get_line_start(offset) + import_idx = self.code.rindex('import', line_start, offset) + imported_names = import_idx + 7 + except ValueError: + return False + # Check if the offset is within the imported names + if (imported_names - 1 > offset or + self._find_import_end(imported_names) < offset): + return False + try: + end = self._find_word_end(offset) + as_end = min(self._find_word_end(end + 1), len(self.code)) + as_start = self._find_word_start(as_end) + return self.code[as_start:as_end + 1] == 'as' + except ValueError: + return False + + def is_a_name_after_from_import(self, offset): + try: + if len(self.code) > offset and self.code[offset] == '\n': + line_start = self._get_line_start(offset - 1) + else: + line_start = self._get_line_start(offset) + last_from = self.code.rindex('from ', line_start, offset) + from_import = self.code.index(' import ', last_from) + from_names = from_import + 8 + except ValueError: + return False + if from_names - 1 > offset: + return False + return self._find_import_end(from_names) >= offset + + def get_from_module(self, offset): + try: + last_from = self.code.rindex('from ', 0, offset) + import_offset = self.code.index(' import ', last_from) + end = self._find_last_non_space_char(import_offset) + return self.get_primary_at(end) + except ValueError: + pass + + def is_from_aliased(self, offset): + if not self.is_a_name_after_from_import(offset): + return False + try: + end = self._find_word_end(offset) + as_end = min(self._find_word_end(end + 1), len(self.code)) + as_start = self._find_word_start(as_end) + return self.code[as_start:as_end + 1] == 'as' + except ValueError: + return False + + def get_from_aliased(self, offset): + try: + end = self._find_word_end(offset) + as_ = self._find_word_end(end + 1) + alias = self._find_word_end(as_ + 1) + start = self._find_word_start(alias) + return self.raw[start:alias + 1] + except ValueError: + pass + + def is_function_keyword_parameter(self, offset): + word_end = self._find_word_end(offset) + if word_end + 1 == len(self.code): + return False + next_char = self._find_first_non_space_char(word_end + 1) + equals = self.code[next_char:next_char + 2] + if equals == '==' or not equals.startswith('='): + return False + word_start = self._find_word_start(offset) + prev_char = self._find_last_non_space_char(word_start - 1) + return prev_char - 1 >= 0 and self.code[prev_char] in ',(' + + def is_on_function_call_keyword(self, offset): + stop = self._get_line_start(offset) + if self._is_id_char(offset): + offset = self._find_word_start(offset) - 1 + offset = self._find_last_non_space_char(offset) + if offset <= stop or self.code[offset] not in '(,': + return False + parens_start = self.find_parens_start_from_inside(offset) + return stop < parens_start + + def find_parens_start_from_inside(self, offset): + stop = self._get_line_start(offset) + while offset > stop: + if self.code[offset] == '(': + break + if self.code[offset] != ',': + offset = self._find_primary_start(offset) + offset -= 1 + return max(stop, offset) + + def is_assigned_here(self, offset): + return self.get_assignment_type(offset) is not None + + def get_assignment_type(self, offset): + # XXX: does not handle tuple assignments + word_end = self._find_word_end(offset) + next_char = self._find_first_non_space_char(word_end + 1) + single = self.code[next_char:next_char + 1] + double = self.code[next_char:next_char + 2] + triple = self.code[next_char:next_char + 3] + if double not in ('==', '<=', '>=', '!='): + for op in [single, double, triple]: + if op.endswith('='): + return op + + def get_primary_range(self, offset): + start = self._find_primary_start(offset) + end = self._find_word_end(offset) + 1 + return (start, end) + + def get_word_range(self, offset): + offset = max(0, offset) + start = self._find_word_start(offset) + end = self._find_word_end(offset) + 1 + return (start, end) + + def get_word_parens_range(self, offset, opening='(', closing=')'): + end = self._find_word_end(offset) + start_parens = self.code.index(opening, end) + index = start_parens + open_count = 0 + while index < len(self.code): + if self.code[index] == opening: + open_count += 1 + if self.code[index] == closing: + open_count -= 1 + if open_count == 0: + return (start_parens, index + 1) + index += 1 + return (start_parens, index) + + def get_parameters(self, first, last): + keywords = [] + args = [] + current = self._find_last_non_space_char(last - 1) + while current > first: + primary_start = current + current = self._find_primary_start(current) + while current != first and (self.code[current] not in '=,' + or self.code[current-1] in '=!<>'): + current = self._find_last_non_space_char(current - 1) + primary = self.raw[current + 1:primary_start + 1].strip() + if self.code[current] == '=': + primary_start = current - 1 + current -= 1 + while current != first and self.code[current] not in ',': + current = self._find_last_non_space_char(current - 1) + param_name = self.raw[current + 1:primary_start + 1].strip() + keywords.append((param_name, primary)) + else: + args.append(primary) + current = self._find_last_non_space_char(current - 1) + args.reverse() + keywords.reverse() + return args, keywords + + def is_assigned_in_a_tuple_assignment(self, offset): + start = self._get_line_start(offset) + end = self._get_line_end(offset) + primary_start = self._find_primary_start(offset) + primary_end = self._find_word_end(offset) + + prev_char_offset = self._find_last_non_space_char(primary_start - 1) + next_char_offset = self._find_first_non_space_char(primary_end + 1) + next_char = prev_char = '' + if prev_char_offset >= start: + prev_char = self.code[prev_char_offset] + if next_char_offset < end: + next_char = self.code[next_char_offset] + try: + equals_offset = self.code.index('=', start, end) + except ValueError: + return False + if prev_char not in '(,' and next_char not in ',)': + return False + parens_start = self.find_parens_start_from_inside(offset) + # XXX: only handling (x, y) = value + return offset < equals_offset and \ + self.code[start:parens_start].strip() == '' + + def get_function_and_args_in_header(self, offset): + offset = self.find_function_offset(offset) + lparens, rparens = self.get_word_parens_range(offset) + return self.raw[offset:rparens + 1] + + def find_function_offset(self, offset, definition='def '): + while True: + offset = self.code.index(definition, offset) + if offset == 0 or not self._is_id_char(offset - 1): + break + offset += 1 + def_ = offset + 4 + return self._find_first_non_space_char(def_) + + def get_lambda_and_args(self, offset): + offset = self.find_function_offset(offset, definition='lambda ') + lparens, rparens = self.get_word_parens_range(offset, opening=' ', + closing=':') + return self.raw[offset:rparens + 1] diff --git a/venv/Lib/site-packages/rope/contrib/__init__.py b/venv/Lib/site-packages/rope/contrib/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0d3f837ef48aa3892a2f0b20aae33f0193449bfe --- /dev/null +++ b/venv/Lib/site-packages/rope/contrib/__init__.py @@ -0,0 +1,7 @@ +"""rope IDE tools package + +This package contains modules that can be used in IDEs +but do not depend on the UI. So these modules will be used +by `rope.ui` modules. + +""" diff --git a/venv/Lib/site-packages/rope/contrib/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/rope/contrib/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..779f43a5bfd071acd4268bc87cc0908f796c3f70 Binary files /dev/null and b/venv/Lib/site-packages/rope/contrib/__pycache__/__init__.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/contrib/__pycache__/autoimport.cpython-37.pyc b/venv/Lib/site-packages/rope/contrib/__pycache__/autoimport.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0214fceaf2072ed6380a0286fcbe53279e7bde28 Binary files /dev/null and b/venv/Lib/site-packages/rope/contrib/__pycache__/autoimport.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/contrib/__pycache__/changestack.cpython-37.pyc b/venv/Lib/site-packages/rope/contrib/__pycache__/changestack.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c9ce91d0aba3d7091074777aa0e14b6c78a84193 Binary files /dev/null and b/venv/Lib/site-packages/rope/contrib/__pycache__/changestack.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/contrib/__pycache__/codeassist.cpython-37.pyc b/venv/Lib/site-packages/rope/contrib/__pycache__/codeassist.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b5f211b036650bcaa360fcb39c7d24bb42c8a0d Binary files /dev/null and b/venv/Lib/site-packages/rope/contrib/__pycache__/codeassist.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/contrib/__pycache__/finderrors.cpython-37.pyc b/venv/Lib/site-packages/rope/contrib/__pycache__/finderrors.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..36bc2cab8c0ec1827348a03e7296ea1933af9a21 Binary files /dev/null and b/venv/Lib/site-packages/rope/contrib/__pycache__/finderrors.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/contrib/__pycache__/findit.cpython-37.pyc b/venv/Lib/site-packages/rope/contrib/__pycache__/findit.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..970254fff5b787056692d2a2fa9de68c0dae5be0 Binary files /dev/null and b/venv/Lib/site-packages/rope/contrib/__pycache__/findit.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/contrib/__pycache__/fixmodnames.cpython-37.pyc b/venv/Lib/site-packages/rope/contrib/__pycache__/fixmodnames.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..82b498d058f3e46a801677020056c2b6fb989d50 Binary files /dev/null and b/venv/Lib/site-packages/rope/contrib/__pycache__/fixmodnames.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/contrib/__pycache__/fixsyntax.cpython-37.pyc b/venv/Lib/site-packages/rope/contrib/__pycache__/fixsyntax.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f66ae8dd7b60788e74c809ce21c8ed38eddd5024 Binary files /dev/null and b/venv/Lib/site-packages/rope/contrib/__pycache__/fixsyntax.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/contrib/__pycache__/generate.cpython-37.pyc b/venv/Lib/site-packages/rope/contrib/__pycache__/generate.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3bfad08e48d18d46be0c76c72d1027c61243d11d Binary files /dev/null and b/venv/Lib/site-packages/rope/contrib/__pycache__/generate.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/contrib/autoimport.py b/venv/Lib/site-packages/rope/contrib/autoimport.py new file mode 100644 index 0000000000000000000000000000000000000000..9670080cf7f3c223f7e84156089c6c45e2a5db5b --- /dev/null +++ b/venv/Lib/site-packages/rope/contrib/autoimport.py @@ -0,0 +1,222 @@ +import re + +from rope.base import builtins +from rope.base import exceptions +from rope.base import libutils +from rope.base import pynames +from rope.base import pyobjects +from rope.base import resources +from rope.base import resourceobserver +from rope.base import taskhandle +from rope.refactor import importutils + + +class AutoImport(object): + """A class for finding the module that provides a name + + This class maintains a cache of global names in python modules. + Note that this cache is not accurate and might be out of date. + + """ + + def __init__(self, project, observe=True, underlined=False): + """Construct an AutoImport object + + If `observe` is `True`, listen for project changes and update + the cache. + + If `underlined` is `True`, underlined names are cached, too. + """ + self.project = project + self.underlined = underlined + self.names = project.data_files.read_data('globalnames') + if self.names is None: + self.names = {} + project.data_files.add_write_hook(self._write) + # XXX: using a filtered observer + observer = resourceobserver.ResourceObserver( + changed=self._changed, moved=self._moved, removed=self._removed) + if observe: + project.add_observer(observer) + + def import_assist(self, starting): + """Return a list of ``(name, module)`` tuples + + This function tries to find modules that have a global name + that starts with `starting`. + """ + # XXX: breaking if gave up! use generators + result = [] + for module in self.names: + for global_name in self.names[module]: + if global_name.startswith(starting): + result.append((global_name, module)) + return result + + def get_modules(self, name): + """Return the list of modules that have global `name`""" + result = [] + for module in self.names: + if name in self.names[module]: + result.append(module) + return result + + def get_all_names(self): + """Return the list of all cached global names""" + result = set() + for module in self.names: + result.update(set(self.names[module])) + return result + + def get_name_locations(self, name): + """Return a list of ``(resource, lineno)`` tuples""" + result = [] + for module in self.names: + if name in self.names[module]: + try: + pymodule = self.project.get_module(module) + if name in pymodule: + pyname = pymodule[name] + module, lineno = pyname.get_definition_location() + if module is not None: + resource = module.get_module().get_resource() + if resource is not None and lineno is not None: + result.append((resource, lineno)) + except exceptions.ModuleNotFoundError: + pass + return result + + def generate_cache(self, resources=None, underlined=None, + task_handle=taskhandle.NullTaskHandle()): + """Generate global name cache for project files + + If `resources` is a list of `rope.base.resource.File`\s, only + those files are searched; otherwise all python modules in the + project are cached. + + """ + if resources is None: + resources = self.project.get_python_files() + job_set = task_handle.create_jobset( + 'Generatig autoimport cache', len(resources)) + for file in resources: + job_set.started_job('Working on <%s>' % file.path) + self.update_resource(file, underlined) + job_set.finished_job() + + def generate_modules_cache(self, modules, underlined=None, + task_handle=taskhandle.NullTaskHandle()): + """Generate global name cache for modules listed in `modules`""" + job_set = task_handle.create_jobset( + 'Generatig autoimport cache for modules', len(modules)) + for modname in modules: + job_set.started_job('Working on <%s>' % modname) + if modname.endswith('.*'): + mod = self.project.find_module(modname[:-2]) + if mod: + for sub in submodules(mod): + self.update_resource(sub, underlined) + else: + self.update_module(modname, underlined) + job_set.finished_job() + + def clear_cache(self): + """Clear all entries in global-name cache + + It might be a good idea to use this function before + regenerating global names. + + """ + self.names.clear() + + def find_insertion_line(self, code): + """Guess at what line the new import should be inserted""" + match = re.search(r'^(def|class)\s+', code) + if match is not None: + code = code[:match.start()] + try: + pymodule = libutils.get_string_module(self.project, code) + except exceptions.ModuleSyntaxError: + return 1 + testmodname = '__rope_testmodule_rope' + importinfo = importutils.NormalImport(((testmodname, None),)) + module_imports = importutils.get_module_imports(self.project, + pymodule) + module_imports.add_import(importinfo) + code = module_imports.get_changed_source() + offset = code.index(testmodname) + lineno = code.count('\n', 0, offset) + 1 + return lineno + + def update_resource(self, resource, underlined=None): + """Update the cache for global names in `resource`""" + try: + pymodule = self.project.get_pymodule(resource) + modname = self._module_name(resource) + self._add_names(pymodule, modname, underlined) + except exceptions.ModuleSyntaxError: + pass + + def update_module(self, modname, underlined=None): + """Update the cache for global names in `modname` module + + `modname` is the name of a module. + """ + try: + pymodule = self.project.get_module(modname) + self._add_names(pymodule, modname, underlined) + except exceptions.ModuleNotFoundError: + pass + + def _module_name(self, resource): + return libutils.modname(resource) + + def _add_names(self, pymodule, modname, underlined): + if underlined is None: + underlined = self.underlined + globals = [] + if isinstance(pymodule, pyobjects.PyDefinedObject): + attributes = pymodule._get_structural_attributes() + else: + attributes = pymodule.get_attributes() + for name, pyname in attributes.items(): + if not underlined and name.startswith('_'): + continue + if isinstance(pyname, (pynames.AssignedName, pynames.DefinedName)): + globals.append(name) + if isinstance(pymodule, builtins.BuiltinModule): + globals.append(name) + self.names[modname] = globals + + def _write(self): + self.project.data_files.write_data('globalnames', self.names) + + def _changed(self, resource): + if not resource.is_folder(): + self.update_resource(resource) + + def _moved(self, resource, newresource): + if not resource.is_folder(): + modname = self._module_name(resource) + if modname in self.names: + del self.names[modname] + self.update_resource(newresource) + + def _removed(self, resource): + if not resource.is_folder(): + modname = self._module_name(resource) + if modname in self.names: + del self.names[modname] + + +def submodules(mod): + if isinstance(mod, resources.File): + if mod.name.endswith('.py') and mod.name != '__init__.py': + return set([mod]) + return set() + if not mod.has_child('__init__.py'): + return set() + result = set([mod]) + for child in mod.get_children(): + result |= submodules(child) + return result diff --git a/venv/Lib/site-packages/rope/contrib/changestack.py b/venv/Lib/site-packages/rope/contrib/changestack.py new file mode 100644 index 0000000000000000000000000000000000000000..70f2271f7c640229788823cdda7c6df18ea787de --- /dev/null +++ b/venv/Lib/site-packages/rope/contrib/changestack.py @@ -0,0 +1,52 @@ +"""For performing many refactorings as a single command + +`changestack` module can be used to perform many refactorings on top +of each other as one bigger command. It can be used like:: + + stack = ChangeStack(project, 'my big command') + + #.. + stack.push(refactoring1.get_changes()) + #.. + stack.push(refactoring2.get_changes()) + #.. + stack.push(refactoringX.get_changes()) + + stack.pop_all() + changes = stack.merged() + +Now `changes` can be previewed or performed as before. +""" + +from rope.base import change + + +class ChangeStack(object): + + def __init__(self, project, description='merged changes'): + self.project = project + self.description = description + self.stack = [] + + def push(self, changes): + self.stack.append(changes) + self.project.do(changes) + + def pop_all(self): + for i in range(len(self.stack)): + self.project.history.undo(drop=True) + + def merged(self): + result = change.ChangeSet(self.description) + for changes in self.stack: + for c in self._basic_changes(changes): + result.add_change(c) + return result + + def _basic_changes(self, changes): + if isinstance(changes, change.ChangeSet): + for child in changes.changes: + for atom in self._basic_changes(child): + yield atom + else: + yield changes diff --git a/venv/Lib/site-packages/rope/contrib/codeassist.py b/venv/Lib/site-packages/rope/contrib/codeassist.py new file mode 100644 index 0000000000000000000000000000000000000000..92c1bfc275c77c0b2985545426f736a5ed37dfde --- /dev/null +++ b/venv/Lib/site-packages/rope/contrib/codeassist.py @@ -0,0 +1,695 @@ +import keyword +import sys +import warnings + +import rope.base.codeanalyze +import rope.base.evaluate +from rope.base import builtins +from rope.base import exceptions +from rope.base import libutils +from rope.base import pynames +from rope.base import pynamesdef +from rope.base import pyobjects +from rope.base import pyobjectsdef +from rope.base import pyscopes +from rope.base import worder +from rope.contrib import fixsyntax +from rope.refactor import functionutils + + +def code_assist(project, source_code, offset, resource=None, + templates=None, maxfixes=1, later_locals=True): + """Return python code completions as a list of `CodeAssistProposal`\s + + `resource` is a `rope.base.resources.Resource` object. If + provided, relative imports are handled. + + `maxfixes` is the maximum number of errors to fix if the code has + errors in it. + + If `later_locals` is `False` names defined in this scope and after + this line is ignored. + + """ + if templates is not None: + warnings.warn('Codeassist no longer supports templates', + DeprecationWarning, stacklevel=2) + assist = _PythonCodeAssist( + project, source_code, offset, resource=resource, + maxfixes=maxfixes, later_locals=later_locals) + return assist() + + +def starting_offset(source_code, offset): + """Return the offset in which the completion should be inserted + + Usually code assist proposals should be inserted like:: + + completion = proposal.name + result = (source_code[:starting_offset] + + completion + source_code[offset:]) + + Where starting_offset is the offset returned by this function. + + """ + word_finder = worder.Worder(source_code, True) + expression, starting, starting_offset = \ + word_finder.get_splitted_primary_before(offset) + return starting_offset + + +def get_doc(project, source_code, offset, resource=None, maxfixes=1): + """Get the pydoc""" + fixer = fixsyntax.FixSyntax(project, source_code, resource, maxfixes) + pyname = fixer.pyname_at(offset) + if pyname is None: + return None + pyobject = pyname.get_object() + return PyDocExtractor().get_doc(pyobject) + + +def get_calltip(project, source_code, offset, resource=None, + maxfixes=1, ignore_unknown=False, remove_self=False): + """Get the calltip of a function + + The format of the returned string is + ``module_name.holding_scope_names.function_name(arguments)``. For + classes `__init__()` and for normal objects `__call__()` function + is used. + + Note that the offset is on the function itself *not* after the its + open parenthesis. (Actually it used to be the other way but it + was easily confused when string literals were involved. So I + decided it is better for it not to try to be too clever when it + cannot be clever enough). You can use a simple search like:: + + offset = source_code.rindex('(', 0, offset) - 1 + + to handle simple situations. + + If `ignore_unknown` is `True`, `None` is returned for functions + without source-code like builtins and extensions. + + If `remove_self` is `True`, the first parameter whose name is self + will be removed for methods. + """ + fixer = fixsyntax.FixSyntax(project, source_code, resource, maxfixes) + pyname = fixer.pyname_at(offset) + if pyname is None: + return None + pyobject = pyname.get_object() + return PyDocExtractor().get_calltip(pyobject, ignore_unknown, remove_self) + + +def get_definition_location(project, source_code, offset, + resource=None, maxfixes=1): + """Return the definition location of the python name at `offset` + + Return a (`rope.base.resources.Resource`, lineno) tuple. If no + `resource` is given and the definition is inside the same module, + the first element of the returned tuple would be `None`. If the + location cannot be determined ``(None, None)`` is returned. + + """ + fixer = fixsyntax.FixSyntax(project, source_code, resource, maxfixes) + pyname = fixer.pyname_at(offset) + if pyname is not None: + module, lineno = pyname.get_definition_location() + if module is not None: + return module.get_module().get_resource(), lineno + return (None, None) + + +def find_occurrences(*args, **kwds): + import rope.contrib.findit + warnings.warn('Use `rope.contrib.findit.find_occurrences()` instead', + DeprecationWarning, stacklevel=2) + return rope.contrib.findit.find_occurrences(*args, **kwds) + + +def get_canonical_path(project, resource, offset): + """Get the canonical path to an object. + + Given the offset of the object, this returns a list of + (name, name_type) tuples representing the canonical path to the + object. For example, the 'x' in the following code: + + class Foo(object): + def bar(self): + class Qux(object): + def mux(self, x): + pass + + we will return: + + [('Foo', 'CLASS'), ('bar', 'FUNCTION'), ('Qux', 'CLASS'), + ('mux', 'FUNCTION'), ('x', 'PARAMETER')] + + `resource` is a `rope.base.resources.Resource` object. + + `offset` is the offset of the pyname you want the path to. + + """ + # Retrieve the PyName. + pymod = project.get_pymodule(resource) + pyname = rope.base.evaluate.eval_location(pymod, offset) + + # Now get the location of the definition and its containing scope. + defmod, lineno = pyname.get_definition_location() + if not defmod: + return None + scope = defmod.get_scope().get_inner_scope_for_line(lineno) + + # Start with the name of the object we're interested in. + names = [] + if isinstance(pyname, pynamesdef.ParameterName): + names = [(worder.get_name_at(pymod.get_resource(), offset), + 'PARAMETER') ] + elif isinstance(pyname, pynamesdef.AssignedName): + names = [(worder.get_name_at(pymod.get_resource(), offset), + 'VARIABLE')] + + # Collect scope names. + while scope.parent: + if isinstance(scope, pyscopes.FunctionScope): + scope_type = 'FUNCTION' + elif isinstance(scope, pyscopes.ClassScope): + scope_type = 'CLASS' + else: + scope_type = None + names.append((scope.pyobject.get_name(), scope_type)) + scope = scope.parent + + names.append((defmod.get_resource().real_path, 'MODULE')) + names.reverse() + return names + + +class CompletionProposal(object): + """A completion proposal + + The `scope` instance variable shows where proposed name came from + and can be 'global', 'local', 'builtin', 'attribute', 'keyword', + 'imported', 'parameter_keyword'. + + The `type` instance variable shows the approximate type of the + proposed object and can be 'instance', 'class', 'function', 'module', + and `None`. + + All possible relations between proposal's `scope` and `type` are shown + in the table below (different scopes in rows and types in columns): + + | instance | class | function | module | None + local | + | + | + | + | + global | + | + | + | + | + builtin | + | + | + | | + attribute | + | + | + | + | + imported | + | + | + | + | + keyword | | | | | + + parameter_keyword | | | | | + + + """ + + def __init__(self, name, scope, pyname=None): + self.name = name + self.pyname = pyname + self.scope = self._get_scope(scope) + + def __str__(self): + return '%s (%s, %s)' % (self.name, self.scope, self.type) + + def __repr__(self): + return str(self) + + @property + def parameters(self): + """The names of the parameters the function takes. + + Returns None if this completion is not a function. + """ + pyname = self.pyname + if isinstance(pyname, pynames.ImportedName): + pyname = pyname._get_imported_pyname() + if isinstance(pyname, pynames.DefinedName): + pyobject = pyname.get_object() + if isinstance(pyobject, pyobjects.AbstractFunction): + return pyobject.get_param_names() + + @property + def type(self): + pyname = self.pyname + if isinstance(pyname, builtins.BuiltinName): + pyobject = pyname.get_object() + if isinstance(pyobject, builtins.BuiltinFunction): + return 'function' + elif isinstance(pyobject, builtins.BuiltinClass): + return 'class' + elif isinstance(pyobject, builtins.BuiltinObject) or \ + isinstance(pyobject, builtins.BuiltinName): + return 'instance' + elif isinstance(pyname, pynames.ImportedModule): + return 'module' + elif isinstance(pyname, pynames.ImportedName) or \ + isinstance(pyname, pynames.DefinedName): + pyobject = pyname.get_object() + if isinstance(pyobject, pyobjects.AbstractFunction): + return 'function' + if isinstance(pyobject, pyobjects.AbstractClass): + return 'class' + return 'instance' + + def _get_scope(self, scope): + if isinstance(self.pyname, builtins.BuiltinName): + return 'builtin' + if isinstance(self.pyname, pynames.ImportedModule) or \ + isinstance(self.pyname, pynames.ImportedName): + return 'imported' + return scope + + def get_doc(self): + """Get the proposed object's docstring. + + Returns None if it can not be get. + """ + if not self.pyname: + return None + pyobject = self.pyname.get_object() + if not hasattr(pyobject, 'get_doc'): + return None + return self.pyname.get_object().get_doc() + + @property + def kind(self): + warnings.warn("the proposal's `kind` property is deprecated, " + "use `scope` instead") + return self.scope + + +# leaved for backward compatibility +CodeAssistProposal = CompletionProposal + + +class NamedParamProposal(CompletionProposal): + """A parameter keyword completion proposal + + Holds reference to ``_function`` -- the function which + parameter ``name`` belongs to. This allows to determine + default value for this parameter. + """ + def __init__(self, name, function): + self.argname = name + name = '%s=' % name + super(NamedParamProposal, self).__init__(name, 'parameter_keyword') + self._function = function + + def get_default(self): + """Get a string representation of a param's default value. + + Returns None if there is no default value for this param. + """ + definfo = functionutils.DefinitionInfo.read(self._function) + for arg, default in definfo.args_with_defaults: + if self.argname == arg: + return default + return None + + +def sorted_proposals(proposals, scopepref=None, typepref=None): + """Sort a list of proposals + + Return a sorted list of the given `CodeAssistProposal`\s. + + `scopepref` can be a list of proposal scopes. Defaults to + ``['parameter_keyword', 'local', 'global', 'imported', + 'attribute', 'builtin', 'keyword']``. + + `typepref` can be a list of proposal types. Defaults to + ``['class', 'function', 'instance', 'module', None]``. + (`None` stands for completions with no type like keywords.) + """ + sorter = _ProposalSorter(proposals, scopepref, typepref) + return sorter.get_sorted_proposal_list() + + +def starting_expression(source_code, offset): + """Return the expression to complete""" + word_finder = worder.Worder(source_code, True) + expression, starting, starting_offset = \ + word_finder.get_splitted_primary_before(offset) + if expression: + return expression + '.' + starting + return starting + + +def default_templates(): + warnings.warn('default_templates() is deprecated.', + DeprecationWarning, stacklevel=2) + return {} + + +class _PythonCodeAssist(object): + + def __init__(self, project, source_code, offset, resource=None, + maxfixes=1, later_locals=True): + self.project = project + self.code = source_code + self.resource = resource + self.maxfixes = maxfixes + self.later_locals = later_locals + self.word_finder = worder.Worder(source_code, True) + self.expression, self.starting, self.offset = \ + self.word_finder.get_splitted_primary_before(offset) + + keywords = keyword.kwlist + + def _find_starting_offset(self, source_code, offset): + current_offset = offset - 1 + while current_offset >= 0 and (source_code[current_offset].isalnum() or + source_code[current_offset] in '_'): + current_offset -= 1 + return current_offset + 1 + + def _matching_keywords(self, starting): + result = [] + for kw in self.keywords: + if kw.startswith(starting): + result.append(CompletionProposal(kw, 'keyword')) + return result + + def __call__(self): + if self.offset > len(self.code): + return [] + completions = list(self._code_completions().values()) + if self.expression.strip() == '' and self.starting.strip() != '': + completions.extend(self._matching_keywords(self.starting)) + return completions + + def _dotted_completions(self, module_scope, holding_scope): + result = {} + found_pyname = rope.base.evaluate.eval_str(holding_scope, + self.expression) + if found_pyname is not None: + element = found_pyname.get_object() + compl_scope = 'attribute' + if isinstance(element, (pyobjectsdef.PyModule, + pyobjectsdef.PyPackage)): + compl_scope = 'imported' + for name, pyname in element.get_attributes().items(): + if name.startswith(self.starting): + result[name] = CompletionProposal(name, compl_scope, + pyname) + return result + + def _undotted_completions(self, scope, result, lineno=None): + if scope.parent is not None: + self._undotted_completions(scope.parent, result) + if lineno is None: + names = scope.get_propagated_names() + else: + names = scope.get_names() + for name, pyname in names.items(): + if name.startswith(self.starting): + compl_scope = 'local' + if scope.get_kind() == 'Module': + compl_scope = 'global' + if lineno is None or self.later_locals or \ + not self._is_defined_after(scope, pyname, lineno): + result[name] = CompletionProposal(name, compl_scope, + pyname) + + def _from_import_completions(self, pymodule): + module_name = self.word_finder.get_from_module(self.offset) + if module_name is None: + return {} + pymodule = self._find_module(pymodule, module_name) + result = {} + for name in pymodule: + if name.startswith(self.starting): + result[name] = CompletionProposal(name, scope='global', + pyname=pymodule[name]) + return result + + def _find_module(self, pymodule, module_name): + dots = 0 + while module_name[dots] == '.': + dots += 1 + pyname = pynames.ImportedModule(pymodule, + module_name[dots:], dots) + return pyname.get_object() + + def _is_defined_after(self, scope, pyname, lineno): + location = pyname.get_definition_location() + if location is not None and location[1] is not None: + if location[0] == scope.pyobject.get_module() and \ + lineno <= location[1] <= scope.get_end(): + return True + + def _code_completions(self): + lineno = self.code.count('\n', 0, self.offset) + 1 + fixer = fixsyntax.FixSyntax(self.project, self.code, + self.resource, self.maxfixes) + pymodule = fixer.get_pymodule() + module_scope = pymodule.get_scope() + code = pymodule.source_code + lines = code.split('\n') + result = {} + start = fixsyntax._logical_start(lines, lineno) + indents = fixsyntax._get_line_indents(lines[start - 1]) + inner_scope = module_scope.get_inner_scope_for_line(start, indents) + if self.word_finder.is_a_name_after_from_import(self.offset): + return self._from_import_completions(pymodule) + if self.expression.strip() != '': + result.update(self._dotted_completions(module_scope, inner_scope)) + else: + result.update(self._keyword_parameters(module_scope.pyobject, + inner_scope)) + self._undotted_completions(inner_scope, result, lineno=lineno) + return result + + def _keyword_parameters(self, pymodule, scope): + offset = self.offset + if offset == 0: + return {} + word_finder = worder.Worder(self.code, True) + if word_finder.is_on_function_call_keyword(offset - 1): + function_parens = word_finder.\ + find_parens_start_from_inside(offset - 1) + primary = word_finder.get_primary_at(function_parens - 1) + try: + function_pyname = rope.base.evaluate.\ + eval_str(scope, primary) + except exceptions.BadIdentifierError: + return {} + if function_pyname is not None: + pyobject = function_pyname.get_object() + if isinstance(pyobject, pyobjects.AbstractFunction): + pass + elif isinstance(pyobject, pyobjects.AbstractClass) and \ + '__init__' in pyobject: + pyobject = pyobject['__init__'].get_object() + elif '__call__' in pyobject: + pyobject = pyobject['__call__'].get_object() + if isinstance(pyobject, pyobjects.AbstractFunction): + param_names = [] + param_names.extend( + pyobject.get_param_names(special_args=False)) + result = {} + for name in param_names: + if name.startswith(self.starting): + result[name + '='] = NamedParamProposal( + name, pyobject + ) + return result + return {} + + +class _ProposalSorter(object): + """Sort a list of code assist proposals""" + + def __init__(self, code_assist_proposals, scopepref=None, typepref=None): + self.proposals = code_assist_proposals + if scopepref is None: + scopepref = ['parameter_keyword', 'local', 'global', 'imported', + 'attribute', 'builtin', 'keyword'] + self.scopepref = scopepref + if typepref is None: + typepref = ['class', 'function', 'instance', 'module', None] + self.typerank = dict((type, index) + for index, type in enumerate(typepref)) + + def get_sorted_proposal_list(self): + """Return a list of `CodeAssistProposal`""" + proposals = {} + for proposal in self.proposals: + proposals.setdefault(proposal.scope, []).append(proposal) + result = [] + for scope in self.scopepref: + scope_proposals = proposals.get(scope, []) + scope_proposals = [proposal for proposal in scope_proposals + if proposal.type in self.typerank] + scope_proposals.sort(key=self._proposal_key) + result.extend(scope_proposals) + return result + + def _proposal_key(self, proposal1): + def _underline_count(name): + return sum(1 for c in name if c == "_") + return (self.typerank.get(proposal1.type, 100), + _underline_count(proposal1.name), + proposal1.name) + #if proposal1.type != proposal2.type: + # return cmp(self.typerank.get(proposal1.type, 100), + # self.typerank.get(proposal2.type, 100)) + #return self._compare_underlined_names(proposal1.name, + # proposal2.name) + + +class PyDocExtractor(object): + + def get_doc(self, pyobject): + if isinstance(pyobject, pyobjects.AbstractFunction): + return self._get_function_docstring(pyobject) + elif isinstance(pyobject, pyobjects.AbstractClass): + return self._get_class_docstring(pyobject) + elif isinstance(pyobject, pyobjects.AbstractModule): + return self._trim_docstring(pyobject.get_doc()) + return None + + def get_calltip(self, pyobject, ignore_unknown=False, remove_self=False): + try: + if isinstance(pyobject, pyobjects.AbstractClass): + pyobject = pyobject['__init__'].get_object() + if not isinstance(pyobject, pyobjects.AbstractFunction): + pyobject = pyobject['__call__'].get_object() + except exceptions.AttributeNotFoundError: + return None + if ignore_unknown and not isinstance(pyobject, pyobjects.PyFunction): + return + if isinstance(pyobject, pyobjects.AbstractFunction): + result = self._get_function_signature(pyobject, add_module=True) + if remove_self and self._is_method(pyobject): + return result.replace('(self)', '()').replace('(self, ', '(') + return result + + def _get_class_docstring(self, pyclass): + contents = self._trim_docstring(pyclass.get_doc(), 2) + supers = [super.get_name() for super in pyclass.get_superclasses()] + doc = 'class %s(%s):\n\n' % (pyclass.get_name(), ', '.join(supers)) \ + + contents + + if '__init__' in pyclass: + init = pyclass['__init__'].get_object() + if isinstance(init, pyobjects.AbstractFunction): + doc += '\n\n' + self._get_single_function_docstring(init) + return doc + + def _get_function_docstring(self, pyfunction): + functions = [pyfunction] + if self._is_method(pyfunction): + functions.extend(self._get_super_methods(pyfunction.parent, + pyfunction.get_name())) + return '\n\n'.join([self._get_single_function_docstring(function) + for function in functions]) + + def _is_method(self, pyfunction): + return isinstance(pyfunction, pyobjects.PyFunction) and \ + isinstance(pyfunction.parent, pyobjects.PyClass) + + def _get_single_function_docstring(self, pyfunction): + signature = self._get_function_signature(pyfunction) + docs = self._trim_docstring(pyfunction.get_doc(), indents=2) + return signature + ':\n\n' + docs + + def _get_super_methods(self, pyclass, name): + result = [] + for super_class in pyclass.get_superclasses(): + if name in super_class: + function = super_class[name].get_object() + if isinstance(function, pyobjects.AbstractFunction): + result.append(function) + result.extend(self._get_super_methods(super_class, name)) + return result + + def _get_function_signature(self, pyfunction, add_module=False): + location = self._location(pyfunction, add_module) + if isinstance(pyfunction, pyobjects.PyFunction): + info = functionutils.DefinitionInfo.read(pyfunction) + return location + info.to_string() + else: + return '%s(%s)' % (location + pyfunction.get_name(), + ', '.join(pyfunction.get_param_names())) + + def _location(self, pyobject, add_module=False): + location = [] + parent = pyobject.parent + while parent and not isinstance(parent, pyobjects.AbstractModule): + location.append(parent.get_name()) + location.append('.') + parent = parent.parent + if add_module: + if isinstance(pyobject, pyobjects.PyFunction): + location.insert(0, self._get_module(pyobject)) + if isinstance(parent, builtins.BuiltinModule): + location.insert(0, parent.get_name() + '.') + return ''.join(location) + + def _get_module(self, pyfunction): + module = pyfunction.get_module() + if module is not None: + resource = module.get_resource() + if resource is not None: + return libutils.modname(resource) + '.' + return '' + + def _trim_docstring(self, docstring, indents=0): + """The sample code from :PEP:`257`""" + if not docstring: + return '' + # Convert tabs to spaces (following normal Python rules) + # and split into a list of lines: + lines = docstring.expandtabs().splitlines() + # Determine minimum indentation (first line doesn't count): + indent = sys.maxsize + for line in lines[1:]: + stripped = line.lstrip() + if stripped: + indent = min(indent, len(line) - len(stripped)) + # Remove indentation (first line is special): + trimmed = [lines[0].strip()] + if indent < sys.maxsize: + for line in lines[1:]: + trimmed.append(line[indent:].rstrip()) + # Strip off trailing and leading blank lines: + while trimmed and not trimmed[-1]: + trimmed.pop() + while trimmed and not trimmed[0]: + trimmed.pop(0) + # Return a single string: + return '\n'.join((' ' * indents + line for line in trimmed)) + + +# Deprecated classes + +class TemplateProposal(CodeAssistProposal): + def __init__(self, name, template): + warnings.warn('TemplateProposal is deprecated.', + DeprecationWarning, stacklevel=2) + super(TemplateProposal, self).__init__(name, 'template') + self.template = template + + +class Template(object): + + def __init__(self, template): + self.template = template + warnings.warn('Template is deprecated.', + DeprecationWarning, stacklevel=2) + + def variables(self): + return [] + + def substitute(self, mapping): + return self.template + + def get_cursor_location(self, mapping): + return len(self.template) diff --git a/venv/Lib/site-packages/rope/contrib/finderrors.py b/venv/Lib/site-packages/rope/contrib/finderrors.py new file mode 100644 index 0000000000000000000000000000000000000000..109a3e8ac724fb1d48773d41ffff0604341b1496 --- /dev/null +++ b/venv/Lib/site-packages/rope/contrib/finderrors.py @@ -0,0 +1,91 @@ +"""Finding bad name and attribute accesses + +`find_errors` function can be used to find possible bad name and +attribute accesses. As an example:: + + errors = find_errors(project, project.get_resource('mod.py')) + for error in errors: + print('%s: %s' % (error.lineno, error.error)) + +prints possible errors for ``mod.py`` file. + +TODO: + +* use task handles +* reporting names at most once +* attributes of extension modules that don't appear in + extension_modules project config can be ignored +* not calling `PyScope.get_inner_scope_for_line()` if it is a + bottleneck; needs profiling +* not reporting occurrences where rope cannot infer the object +* rope saves multiple objects for some of the names in its objectdb + use all of them not to give false positives +* ... ;-) + +""" +from rope.base import ast, evaluate, pyobjects + + +def find_errors(project, resource): + """Find possible bad name and attribute accesses + + It returns a list of `Error`\s. + """ + pymodule = project.get_pymodule(resource) + finder = _BadAccessFinder(pymodule) + ast.walk(pymodule.get_ast(), finder) + return finder.errors + + +class _BadAccessFinder(object): + + def __init__(self, pymodule): + self.pymodule = pymodule + self.scope = pymodule.get_scope() + self.errors = [] + + def _Name(self, node): + if isinstance(node.ctx, (ast.Store, ast.Param)): + return + scope = self.scope.get_inner_scope_for_line(node.lineno) + pyname = scope.lookup(node.id) + if pyname is None: + self._add_error(node, 'Unresolved variable') + elif self._is_defined_after(scope, pyname, node.lineno): + self._add_error(node, 'Defined later') + + def _Attribute(self, node): + if not isinstance(node.ctx, ast.Store): + scope = self.scope.get_inner_scope_for_line(node.lineno) + pyname = evaluate.eval_node(scope, node.value) + if pyname is not None and \ + pyname.get_object() != pyobjects.get_unknown(): + if node.attr not in pyname.get_object(): + self._add_error(node, 'Unresolved attribute') + ast.walk(node.value, self) + + def _add_error(self, node, msg): + if isinstance(node, ast.Attribute): + name = node.attr + else: + name = node.id + if name != 'None': + error = Error(node.lineno, msg + ' ' + name) + self.errors.append(error) + + def _is_defined_after(self, scope, pyname, lineno): + location = pyname.get_definition_location() + if location is not None and location[1] is not None: + if location[0] == self.pymodule and \ + lineno <= location[1] <= scope.get_end(): + return True + + +class Error(object): + + def __init__(self, lineno, error): + self.lineno = lineno + self.error = error + + def __str__(self): + return '%s: %s' % (self.lineno, self.error) diff --git a/venv/Lib/site-packages/rope/contrib/findit.py b/venv/Lib/site-packages/rope/contrib/findit.py new file mode 100644 index 0000000000000000000000000000000000000000..93eb01a8467649de1f094e7eab61ef49ff4940fb --- /dev/null +++ b/venv/Lib/site-packages/rope/contrib/findit.py @@ -0,0 +1,114 @@ +import rope.base.codeanalyze +import rope.base.evaluate +import rope.base.pyobjects +from rope.base import taskhandle, exceptions, worder +from rope.contrib import fixsyntax +from rope.refactor import occurrences + + +def find_occurrences(project, resource, offset, unsure=False, resources=None, + in_hierarchy=False, + task_handle=taskhandle.NullTaskHandle()): + """Return a list of `Location`\s + + If `unsure` is `True`, possible matches are returned, too. You + can use `Location.unsure` to see which are unsure occurrences. + `resources` can be a list of `rope.base.resource.File`\s that + should be searched for occurrences; if `None` all python files + in the project are searched. + + """ + name = worder.get_name_at(resource, offset) + this_pymodule = project.get_pymodule(resource) + primary, pyname = rope.base.evaluate.eval_location2( + this_pymodule, offset) + + def is_match(occurrence): + return unsure + finder = occurrences.create_finder( + project, name, pyname, unsure=is_match, + in_hierarchy=in_hierarchy, instance=primary) + if resources is None: + resources = project.get_python_files() + job_set = task_handle.create_jobset('Finding Occurrences', + count=len(resources)) + return _find_locations(finder, resources, job_set) + + +def find_implementations(project, resource, offset, resources=None, + task_handle=taskhandle.NullTaskHandle()): + """Find the places a given method is overridden. + + Finds the places a method is implemented. Returns a list of + `Location`\s. + """ + name = worder.get_name_at(resource, offset) + this_pymodule = project.get_pymodule(resource) + pyname = rope.base.evaluate.eval_location(this_pymodule, offset) + if pyname is not None: + pyobject = pyname.get_object() + if not isinstance(pyobject, rope.base.pyobjects.PyFunction) or \ + pyobject.get_kind() != 'method': + raise exceptions.BadIdentifierError('Not a method!') + else: + raise exceptions.BadIdentifierError('Cannot resolve the identifier!') + + def is_defined(occurrence): + if not occurrence.is_defined(): + return False + + def not_self(occurrence): + if occurrence.get_pyname().get_object() == pyname.get_object(): + return False + filters = [is_defined, not_self, + occurrences.InHierarchyFilter(pyname, True)] + finder = occurrences.Finder(project, name, filters=filters) + if resources is None: + resources = project.get_python_files() + job_set = task_handle.create_jobset('Finding Implementations', + count=len(resources)) + return _find_locations(finder, resources, job_set) + + +def find_definition(project, code, offset, resource=None, maxfixes=1): + """Return the definition location of the python name at `offset` + + A `Location` object is returned if the definition location can be + determined, otherwise ``None`` is returned. + """ + fixer = fixsyntax.FixSyntax(project, code, resource, maxfixes) + pyname = fixer.pyname_at(offset) + if pyname is not None: + module, lineno = pyname.get_definition_location() + name = rope.base.worder.Worder(code).get_word_at(offset) + if lineno is not None: + start = module.lines.get_line_start(lineno) + + def check_offset(occurrence): + if occurrence.offset < start: + return False + pyname_filter = occurrences.PyNameFilter(pyname) + finder = occurrences.Finder(project, name, + [check_offset, pyname_filter]) + for occurrence in finder.find_occurrences(pymodule=module): + return Location(occurrence) + + +class Location(object): + + def __init__(self, occurrence): + self.resource = occurrence.resource + self.region = occurrence.get_word_range() + self.offset = self.region[0] + self.unsure = occurrence.is_unsure() + self.lineno = occurrence.lineno + + +def _find_locations(finder, resources, job_set): + result = [] + for resource in resources: + job_set.started_job(resource.path) + for occurrence in finder.find_occurrences(resource): + result.append(Location(occurrence)) + job_set.finished_job() + return result diff --git a/venv/Lib/site-packages/rope/contrib/fixmodnames.py b/venv/Lib/site-packages/rope/contrib/fixmodnames.py new file mode 100644 index 0000000000000000000000000000000000000000..d8bd3da109e09cc852e497da77c4d32c1c714060 --- /dev/null +++ b/venv/Lib/site-packages/rope/contrib/fixmodnames.py @@ -0,0 +1,69 @@ +"""Fix the name of modules + +This module is useful when you want to rename many of the modules in +your project. That can happen specially when you want to change their +naming style. + +For instance:: + + fixer = FixModuleNames(project) + changes = fixer.get_changes(fixer=str.lower) + project.do(changes) + +Here it renames all modules and packages to use lower-cased chars. +You can tell it to use any other style by using the ``fixer`` +argument. + +""" +from rope.base import taskhandle +from rope.contrib import changestack +from rope.refactor import rename + + +class FixModuleNames(object): + + def __init__(self, project): + self.project = project + + def get_changes(self, fixer=str.lower, + task_handle=taskhandle.NullTaskHandle()): + """Fix module names + + `fixer` is a function that takes and returns a `str`. Given + the name of a module, it should return the fixed name. + + """ + stack = changestack.ChangeStack(self.project, 'Fixing module names') + jobset = task_handle.create_jobset('Fixing module names', + self._count_fixes(fixer) + 1) + try: + while True: + for resource in self._tobe_fixed(fixer): + jobset.started_job(resource.path) + renamer = rename.Rename(self.project, resource) + changes = renamer.get_changes(fixer(self._name(resource))) + stack.push(changes) + jobset.finished_job() + break + else: + break + finally: + jobset.started_job('Reverting to original state') + stack.pop_all() + jobset.finished_job() + return stack.merged() + + def _count_fixes(self, fixer): + return len(list(self._tobe_fixed(fixer))) + + def _tobe_fixed(self, fixer): + for resource in self.project.get_python_files(): + modname = self._name(resource) + if modname != fixer(modname): + yield resource + + def _name(self, resource): + modname = resource.name.rsplit('.', 1)[0] + if modname == '__init__': + modname = resource.parent.name + return modname diff --git a/venv/Lib/site-packages/rope/contrib/fixsyntax.py b/venv/Lib/site-packages/rope/contrib/fixsyntax.py new file mode 100644 index 0000000000000000000000000000000000000000..fa2a17d93c1289bc85ce7d99188d4a454204093c --- /dev/null +++ b/venv/Lib/site-packages/rope/contrib/fixsyntax.py @@ -0,0 +1,181 @@ +import rope.base.codeanalyze +import rope.base.evaluate +from rope.base import exceptions +from rope.base import libutils +from rope.base import utils +from rope.base import worder +from rope.base.codeanalyze import ArrayLinesAdapter, LogicalLineFinder + + +class FixSyntax(object): + + def __init__(self, project, code, resource, maxfixes=1): + self.project = project + self.code = code + self.resource = resource + self.maxfixes = maxfixes + + @utils.saveit + def get_pymodule(self): + """Get a `PyModule`""" + msg = None + code = self.code + tries = 0 + while True: + try: + if tries == 0 and self.resource is not None and \ + self.resource.read() == code: + return self.project.get_pymodule(self.resource, + force_errors=True) + return libutils.get_string_module( + self.project, code, resource=self.resource, + force_errors=True) + except exceptions.ModuleSyntaxError as e: + if msg is None: + msg = '%s:%s %s' % (e.filename, e.lineno, e.message_) + if tries < self.maxfixes: + tries += 1 + self.commenter.comment(e.lineno) + code = '\n'.join(self.commenter.lines) + else: + raise exceptions.ModuleSyntaxError( + e.filename, e.lineno, + 'Failed to fix error: {0}'.format(msg)) + + @property + @utils.saveit + def commenter(self): + return _Commenter(self.code) + + def pyname_at(self, offset): + pymodule = self.get_pymodule() + + def old_pyname(): + word_finder = worder.Worder(self.code, True) + expression = word_finder.get_primary_at(offset) + expression = expression.replace('\\\n', ' ').replace('\n', ' ') + lineno = self.code.count('\n', 0, offset) + scope = pymodule.get_scope().get_inner_scope_for_line(lineno) + return rope.base.evaluate.eval_str(scope, expression) + new_code = pymodule.source_code + + def new_pyname(): + newoffset = self.commenter.transfered_offset(offset) + return rope.base.evaluate.eval_location(pymodule, newoffset) + if new_code.startswith(self.code[:offset + 1]): + return new_pyname() + result = old_pyname() + if result is None: + return new_pyname() + return result + + +class _Commenter(object): + + def __init__(self, code): + self.code = code + self.lines = self.code.split('\n') + self.lines.append('\n') + self.origs = list(range(len(self.lines) + 1)) + self.diffs = [0] * (len(self.lines) + 1) + + def comment(self, lineno): + start = _logical_start(self.lines, lineno, check_prev=True) - 1 + # using self._get_stmt_end() instead of self._get_block_end() + # to lower commented lines + end = self._get_stmt_end(start) + indents = _get_line_indents(self.lines[start]) + if 0 < start: + last_lineno = self._last_non_blank(start - 1) + last_line = self.lines[last_lineno] + if last_line.rstrip().endswith(':'): + indents = _get_line_indents(last_line) + 4 + self._set(start, ' ' * indents + 'pass') + for line in range(start + 1, end + 1): + self._set(line, self.lines[start]) + self._fix_incomplete_try_blocks(lineno, indents) + + def transfered_offset(self, offset): + lineno = self.code.count('\n', 0, offset) + diff = sum(self.diffs[:lineno]) + return offset + diff + + def _last_non_blank(self, start): + while start > 0 and self.lines[start].strip() == '': + start -= 1 + return start + + def _get_block_end(self, lineno): + end_line = lineno + base_indents = _get_line_indents(self.lines[lineno]) + for i in range(lineno + 1, len(self.lines)): + if _get_line_indents(self.lines[i]) >= base_indents: + end_line = i + else: + break + return end_line + + def _get_stmt_end(self, lineno): + base_indents = _get_line_indents(self.lines[lineno]) + for i in range(lineno + 1, len(self.lines)): + if _get_line_indents(self.lines[i]) <= base_indents: + return i - 1 + return lineno + + def _fix_incomplete_try_blocks(self, lineno, indents): + block_start = lineno + last_indents = indents + while block_start > 0: + block_start = rope.base.codeanalyze.get_block_start( + ArrayLinesAdapter(self.lines), block_start) - 1 + if self.lines[block_start].strip().startswith('try:'): + indents = _get_line_indents(self.lines[block_start]) + if indents > last_indents: + continue + last_indents = indents + block_end = self._find_matching_deindent(block_start) + line = self.lines[block_end].strip() + if not (line.startswith('finally:') or + line.startswith('except ') or + line.startswith('except:')): + self._insert(block_end, ' ' * indents + 'finally:') + self._insert(block_end + 1, ' ' * indents + ' pass') + + def _find_matching_deindent(self, line_number): + indents = _get_line_indents(self.lines[line_number]) + current_line = line_number + 1 + while current_line < len(self.lines): + line = self.lines[current_line] + if not line.strip().startswith('#') and not line.strip() == '': + # HACK: We should have used logical lines here + if _get_line_indents(self.lines[current_line]) <= indents: + return current_line + current_line += 1 + return len(self.lines) - 1 + + def _set(self, lineno, line): + self.diffs[self.origs[lineno]] += len(line) - len(self.lines[lineno]) + self.lines[lineno] = line + + def _insert(self, lineno, line): + self.diffs[self.origs[lineno]] += len(line) + 1 + self.origs.insert(lineno, self.origs[lineno]) + self.lines.insert(lineno, line) + + +def _logical_start(lines, lineno, check_prev=False): + logical_finder = LogicalLineFinder(ArrayLinesAdapter(lines)) + if check_prev: + prev = lineno - 1 + while prev > 0: + start, end = logical_finder.logical_line_in(prev) + if end is None or start <= lineno < end: + return start + if start <= prev: + break + prev -= 1 + return logical_finder.logical_line_in(lineno)[0] + + +def _get_line_indents(line): + return rope.base.codeanalyze.count_line_indents(line) diff --git a/venv/Lib/site-packages/rope/contrib/generate.py b/venv/Lib/site-packages/rope/contrib/generate.py new file mode 100644 index 0000000000000000000000000000000000000000..9291a1d164ab05cc4355e1b6d24c7ec54db3e458 --- /dev/null +++ b/venv/Lib/site-packages/rope/contrib/generate.py @@ -0,0 +1,379 @@ +import rope.base.evaluate +from rope.base import libutils +from rope.base import (change, pyobjects, exceptions, pynames, worder, + codeanalyze) +from rope.refactor import sourceutils, importutils, functionutils, suites + + +def create_generate(kind, project, resource, offset): + """A factory for creating `Generate` objects + + `kind` can be 'variable', 'function', 'class', 'module' or + 'package'. + + """ + generate = eval('Generate' + kind.title()) + return generate(project, resource, offset) + + +def create_module(project, name, sourcefolder=None): + """Creates a module and returns a `rope.base.resources.File`""" + if sourcefolder is None: + sourcefolder = project.root + packages = name.split('.') + parent = sourcefolder + for package in packages[:-1]: + parent = parent.get_child(package) + return parent.create_file(packages[-1] + '.py') + + +def create_package(project, name, sourcefolder=None): + """Creates a package and returns a `rope.base.resources.Folder`""" + if sourcefolder is None: + sourcefolder = project.root + packages = name.split('.') + parent = sourcefolder + for package in packages[:-1]: + parent = parent.get_child(package) + made_packages = parent.create_folder(packages[-1]) + made_packages.create_file('__init__.py') + return made_packages + + +class _Generate(object): + + def __init__(self, project, resource, offset, goal_resource=None): + self.project = project + self.resource = resource + self.goal_resource = goal_resource + self.info = self._generate_info(project, resource, offset) + self.name = self.info.get_name() + self._check_exceptional_conditions() + + def _generate_info(self, project, resource, offset): + return _GenerationInfo(project.pycore, resource, offset, self.goal_resource) + + def _check_exceptional_conditions(self): + if self.info.element_already_exists(): + raise exceptions.RefactoringError( + 'Element <%s> already exists.' % self.name) + if not self.info.primary_is_found(): + raise exceptions.RefactoringError( + 'Cannot determine the scope <%s> should be defined in.' % + self.name) + + def get_changes(self): + changes = change.ChangeSet('Generate %s <%s>' % + (self._get_element_kind(), self.name)) + indents = self.info.get_scope_indents() + blanks = self.info.get_blank_lines() + base_definition = sourceutils.fix_indentation(self._get_element(), + indents) + definition = '\n' * blanks[0] + base_definition + '\n' * blanks[1] + + resource = self.info.get_insertion_resource() + start, end = self.info.get_insertion_offsets() + + collector = codeanalyze.ChangeCollector(resource.read()) + collector.add_change(start, end, definition) + changes.add_change(change.ChangeContents( + resource, collector.get_changed())) + if self.goal_resource: + relative_import = _add_relative_import_to_module(self.project, self.resource, self.goal_resource, self.name) + changes.add_change(relative_import) + return changes + + def get_location(self): + return (self.info.get_insertion_resource(), + self.info.get_insertion_lineno()) + + def _get_element_kind(self): + raise NotImplementedError() + + def _get_element(self): + raise NotImplementedError() + + +class GenerateFunction(_Generate): + + def _generate_info(self, project, resource, offset): + return _FunctionGenerationInfo(project.pycore, resource, offset) + + def _get_element(self): + decorator = '' + args = [] + if self.info.is_static_method(): + decorator = '@staticmethod\n' + if self.info.is_method() or self.info.is_constructor() or \ + self.info.is_instance(): + args.append('self') + args.extend(self.info.get_passed_args()) + definition = '%sdef %s(%s):\n pass\n' % (decorator, self.name, + ', '.join(args)) + return definition + + def _get_element_kind(self): + return 'Function' + + +class GenerateVariable(_Generate): + + def _get_element(self): + return '%s = None\n' % self.name + + def _get_element_kind(self): + return 'Variable' + + +class GenerateClass(_Generate): + + def _get_element(self): + return 'class %s(object):\n pass\n' % self.name + + def _get_element_kind(self): + return 'Class' + + +class GenerateModule(_Generate): + + def get_changes(self): + package = self.info.get_package() + changes = change.ChangeSet('Generate Module <%s>' % self.name) + new_resource = self.project.get_file('%s/%s.py' % + (package.path, self.name)) + if new_resource.exists(): + raise exceptions.RefactoringError( + 'Module <%s> already exists' % new_resource.path) + changes.add_change(change.CreateResource(new_resource)) + changes.add_change(_add_import_to_module( + self.project, self.resource, new_resource)) + return changes + + def get_location(self): + package = self.info.get_package() + return (package.get_child('%s.py' % self.name), 1) + + +class GeneratePackage(_Generate): + + def get_changes(self): + package = self.info.get_package() + changes = change.ChangeSet('Generate Package <%s>' % self.name) + new_resource = self.project.get_folder('%s/%s' % + (package.path, self.name)) + if new_resource.exists(): + raise exceptions.RefactoringError( + 'Package <%s> already exists' % new_resource.path) + changes.add_change(change.CreateResource(new_resource)) + changes.add_change(_add_import_to_module( + self.project, self.resource, new_resource)) + child = self.project.get_folder(package.path + '/' + self.name) + changes.add_change(change.CreateFile(child, '__init__.py')) + return changes + + def get_location(self): + package = self.info.get_package() + child = package.get_child(self.name) + return (child.get_child('__init__.py'), 1) + + +def _add_import_to_module(project, resource, imported): + pymodule = project.get_pymodule(resource) + import_tools = importutils.ImportTools(project) + module_imports = import_tools.module_imports(pymodule) + module_name = libutils.modname(imported) + new_import = importutils.NormalImport(((module_name, None), )) + module_imports.add_import(new_import) + return change.ChangeContents(resource, module_imports.get_changed_source()) + + +def _add_relative_import_to_module(project, resource, imported, name): + pymodule = project.get_pymodule(resource) + import_tools = importutils.ImportTools(project) + module_imports = import_tools.module_imports(pymodule) + new_import = import_tools.get_from_import(imported, name) + module_imports.add_import(new_import) + return change.ChangeContents(resource, module_imports.get_changed_source()) + + +class _GenerationInfo(object): + + def __init__(self, pycore, resource, offset, goal_resource=None): + self.pycore = pycore + self.resource = resource + self.offset = offset + self.goal_resource = goal_resource + self.source_pymodule = self.pycore.project.get_pymodule(resource) + finder = rope.base.evaluate.ScopeNameFinder(self.source_pymodule) + self.primary, self.pyname = finder.get_primary_and_pyname_at(offset) + self._init_fields() + + def _init_fields(self): + self.source_scope = self._get_source_scope() + self.goal_scope = self._get_goal_scope() + self.goal_pymodule = self._get_goal_module(self.goal_scope) + + def _get_goal_scope(self): + if self.primary is None: + if self.goal_resource: + return self.pycore.project.get_pymodule(self.goal_resource).get_scope() + else: + return self._get_source_scope() + pyobject = self.primary.get_object() + if isinstance(pyobject, pyobjects.PyDefinedObject): + return pyobject.get_scope() + elif isinstance(pyobject.get_type(), pyobjects.PyClass): + return pyobject.get_type().get_scope() + + def _get_goal_module(self, scope): + if scope is None: + return + while scope.parent is not None: + scope = scope.parent + return scope.pyobject + + def _get_source_scope(self): + module_scope = self.source_pymodule.get_scope() + lineno = self.source_pymodule.lines.get_line_number(self.offset) + return module_scope.get_inner_scope_for_line(lineno) + + def get_insertion_lineno(self): + lines = self.goal_pymodule.lines + if self.goal_scope == self.source_scope: + line_finder = self.goal_pymodule.logical_lines + lineno = lines.get_line_number(self.offset) + lineno = line_finder.logical_line_in(lineno)[0] + root = suites.ast_suite_tree(self.goal_scope.pyobject.get_ast()) + suite = root.find_suite(lineno) + indents = sourceutils.get_indents(lines, lineno) + while self.get_scope_indents() < indents: + lineno = suite.get_start() + indents = sourceutils.get_indents(lines, lineno) + suite = suite.parent + return lineno + else: + return min(self.goal_scope.get_end() + 1, lines.length()) + + def get_insertion_resource(self): + return self.goal_pymodule.get_resource() + + def get_insertion_offsets(self): + if self.goal_scope.get_kind() == 'Class': + start, end = sourceutils.get_body_region(self.goal_scope.pyobject) + if self.goal_pymodule.source_code[start:end].strip() == 'pass': + return start, end + lines = self.goal_pymodule.lines + start = lines.get_line_start(self.get_insertion_lineno()) + return (start, start) + + def get_scope_indents(self): + if self.goal_scope.get_kind() == 'Module': + return 0 + return sourceutils.get_indents(self.goal_pymodule.lines, + self.goal_scope.get_start()) + 4 + + def get_blank_lines(self): + if self.goal_scope.get_kind() == 'Module': + base_blanks = 2 + if self.goal_pymodule.source_code.strip() == '': + base_blanks = 0 + if self.goal_scope.get_kind() == 'Class': + base_blanks = 1 + if self.goal_scope.get_kind() == 'Function': + base_blanks = 0 + if self.goal_scope == self.source_scope: + return (0, base_blanks) + return (base_blanks, 0) + + def get_package(self): + primary = self.primary + if self.primary is None: + return self.pycore.project.get_source_folders()[0] + if isinstance(primary.get_object(), pyobjects.PyPackage): + return primary.get_object().get_resource() + raise exceptions.RefactoringError( + 'A module/package can be only created in a package.') + + def primary_is_found(self): + return self.goal_scope is not None + + def element_already_exists(self): + if self.pyname is None or isinstance(self.pyname, pynames.UnboundName): + return False + return self.get_name() in self.goal_scope.get_defined_names() + + def get_name(self): + return worder.get_name_at(self.resource, self.offset) + + +class _FunctionGenerationInfo(_GenerationInfo): + + def _get_goal_scope(self): + if self.is_constructor(): + return self.pyname.get_object().get_scope() + if self.is_instance(): + return self.pyname.get_object().get_type().get_scope() + if self.primary is None: + return self._get_source_scope() + pyobject = self.primary.get_object() + if isinstance(pyobject, pyobjects.PyDefinedObject): + return pyobject.get_scope() + elif isinstance(pyobject.get_type(), pyobjects.PyClass): + return pyobject.get_type().get_scope() + + def element_already_exists(self): + if self.pyname is None or isinstance(self.pyname, pynames.UnboundName): + return False + return self.get_name() in self.goal_scope.get_defined_names() + + def is_static_method(self): + return self.primary is not None and \ + isinstance(self.primary.get_object(), pyobjects.PyClass) + + def is_method(self): + return self.primary is not None and \ + isinstance(self.primary.get_object().get_type(), pyobjects.PyClass) + + def is_constructor(self): + return self.pyname is not None and \ + isinstance(self.pyname.get_object(), pyobjects.PyClass) + + def is_instance(self): + if self.pyname is None: + return False + pyobject = self.pyname.get_object() + return isinstance(pyobject.get_type(), pyobjects.PyClass) + + def get_name(self): + if self.is_constructor(): + return '__init__' + if self.is_instance(): + return '__call__' + return worder.get_name_at(self.resource, self.offset) + + def get_passed_args(self): + result = [] + source = self.source_pymodule.source_code + finder = worder.Worder(source) + if finder.is_a_function_being_called(self.offset): + start, end = finder.get_primary_range(self.offset) + parens_start, parens_end = finder.get_word_parens_range(end - 1) + call = source[start:parens_end] + parser = functionutils._FunctionParser(call, False) + args, keywords = parser.get_parameters() + for arg in args: + if self._is_id(arg): + result.append(arg) + else: + result.append('arg%d' % len(result)) + for name, value in keywords: + result.append(name) + return result + + def _is_id(self, arg): + def id_or_underline(c): + return c.isalpha() or c == '_' + for c in arg: + if not id_or_underline(c) and not c.isdigit(): + return False + return id_or_underline(arg[0]) diff --git a/venv/Lib/site-packages/rope/refactor/__init__.py b/venv/Lib/site-packages/rope/refactor/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4ef6751348145de962ee58c9074927d99590829f --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/__init__.py @@ -0,0 +1,55 @@ +"""rope refactor package + +This package contains modules that perform python refactorings. +Refactoring classes perform refactorings in 4 steps: + +1. Collect some data for performing the refactoring and use them + to construct a refactoring class. Like:: + + renamer = Rename(project, resource, offset) + +2. Some refactorings give you useful information about the + refactoring after their construction. Like:: + + print(renamer.get_old_name()) + +3. Give the refactoring class more information about how to + perform the refactoring and get the changes this refactoring is + going to make. This is done by calling `get_changes` method of the + refactoring class. Like:: + + changes = renamer.get_changes(new_name) + +4. You can commit the changes. Like:: + + project.do(changes) + +These steps are like the steps IDEs usually do for performing a +refactoring. These are the things an IDE does in each step: + +1. Construct a refactoring object by giving it information like + resource, offset and ... . Some of the refactoring problems (like + performing rename refactoring on language keywords) can be reported + here. +2. Print some information about the refactoring and ask the user + about the information that are necessary for completing the + refactoring (like new name). +3. Call the `get_changes` by passing it information asked from + the user (if necessary) and get and preview the changes returned by + it. +4. perform the refactoring. + +From ``0.5m5`` release the `get_changes()` method of some time- +consuming refactorings take an optional `rope.base.taskhandle. +TaskHandle` parameter. You can use this object for stopping or +monitoring the progress of refactorings. + +""" +from rope.refactor.importutils import ImportOrganizer # noqa +from rope.refactor.topackage import ModuleToPackage # noqa + + +__all__ = ['rename', 'move', 'inline', 'extract', 'restructure', 'topackage', + 'importutils', 'usefunction', 'change_signature', + 'encapsulate_field', 'introduce_factory', 'introduce_parameter', + 'localtofield', 'method_object', 'multiproject'] diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9808d74cae4ab7e46cdc97327ed55dd21e6e308c Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/__init__.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/change_signature.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/change_signature.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8ff9464a0484301db3db688574ee85af1146a3a5 Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/change_signature.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/encapsulate_field.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/encapsulate_field.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..92d8bfbac71895ad01f9785d02d53d4a6e5afb7d Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/encapsulate_field.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/extract.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/extract.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a42b8730ca18ee89a72211e77874786d500fde1c Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/extract.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/functionutils.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/functionutils.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3064e76d96f2d4ee2d7d88624192793c30c3ca0a Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/functionutils.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/inline.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/inline.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..13d4ccbea89f8b7e31f17acfa322a1649e0fa01d Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/inline.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/introduce_factory.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/introduce_factory.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bee7d578c35fd432afd55b1c5e53d68d4fbe77bd Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/introduce_factory.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/introduce_parameter.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/introduce_parameter.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a47f5a3d1a1ca1cba3d0a08f1071c5d4872c712 Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/introduce_parameter.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/localtofield.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/localtofield.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c28f70b8d571ec1af9c8d8edaca14207da6661f2 Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/localtofield.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/method_object.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/method_object.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8847338d155d8bbe8682b781a7347a3908addb08 Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/method_object.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/move.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/move.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d4febfa8b521b29d10d4e9fc02091d64f8a2a86 Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/move.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/multiproject.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/multiproject.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b0d100999e21e344d9e35481118c96ef7a6c2fed Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/multiproject.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/occurrences.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/occurrences.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bf3bf986205435719da7dbfe9bfbc64bfd27bfe7 Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/occurrences.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/patchedast.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/patchedast.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f83cc32564a801a486808dbfc3aa45feb01409f1 Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/patchedast.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/rename.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/rename.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..248e4ed5147ff7a6dfb927c34adaabda93a9f995 Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/rename.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/restructure.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/restructure.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1aa58dcf0d0babcf25ce181d75291c4f1616b009 Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/restructure.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/similarfinder.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/similarfinder.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d612877fb2d44f85850fa8169663983812d1e0f Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/similarfinder.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/sourceutils.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/sourceutils.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5f091d6f4328069bc6b272506669f9e529453839 Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/sourceutils.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/suites.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/suites.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b21aab05b50caaa82a1efbc2ee36eeb752664b19 Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/suites.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/topackage.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/topackage.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..152ab6b4b49157571e497e5271cb3d9e0ae159e2 Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/topackage.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/usefunction.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/usefunction.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c51aa1997e6aa9b2039900bc7aa5a6ac29650f3b Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/usefunction.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/__pycache__/wildcards.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/__pycache__/wildcards.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aa139ce4d7814c0b44a5dcc271e83240f51e88de Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/__pycache__/wildcards.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/change_signature.py b/venv/Lib/site-packages/rope/refactor/change_signature.py new file mode 100644 index 0000000000000000000000000000000000000000..b5ba1856a80746cef2ff8c2cbeb84fd296138fbc --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/change_signature.py @@ -0,0 +1,352 @@ +import copy + +import rope.base.exceptions +from rope.base import codeanalyze +from rope.base import evaluate +from rope.base import pyobjects +from rope.base import taskhandle +from rope.base import utils +from rope.base import worder +from rope.base.change import ChangeContents, ChangeSet +from rope.refactor import occurrences, functionutils + + +class ChangeSignature(object): + + def __init__(self, project, resource, offset): + self.project = project + self.resource = resource + self.offset = offset + self._set_name_and_pyname() + if self.pyname is None or self.pyname.get_object() is None or \ + not isinstance(self.pyname.get_object(), pyobjects.PyFunction): + raise rope.base.exceptions.RefactoringError( + 'Change method signature should be performed on functions') + + def _set_name_and_pyname(self): + self.name = worder.get_name_at(self.resource, self.offset) + this_pymodule = self.project.get_pymodule(self.resource) + self.primary, self.pyname = evaluate.eval_location2( + this_pymodule, self.offset) + if self.pyname is None: + return + pyobject = self.pyname.get_object() + if isinstance(pyobject, pyobjects.PyClass) and \ + '__init__' in pyobject: + self.pyname = pyobject['__init__'] + self.name = '__init__' + pyobject = self.pyname.get_object() + self.others = None + if self.name == '__init__' and \ + isinstance(pyobject, pyobjects.PyFunction) and \ + isinstance(pyobject.parent, pyobjects.PyClass): + pyclass = pyobject.parent + self.others = (pyclass.get_name(), + pyclass.parent[pyclass.get_name()]) + + def _change_calls(self, call_changer, in_hierarchy=None, resources=None, + handle=taskhandle.NullTaskHandle()): + if resources is None: + resources = self.project.get_python_files() + changes = ChangeSet('Changing signature of <%s>' % self.name) + job_set = handle.create_jobset('Collecting Changes', len(resources)) + finder = occurrences.create_finder( + self.project, self.name, self.pyname, instance=self.primary, + in_hierarchy=in_hierarchy and self.is_method()) + if self.others: + name, pyname = self.others + constructor_finder = occurrences.create_finder( + self.project, name, pyname, only_calls=True) + finder = _MultipleFinders([finder, constructor_finder]) + for file in resources: + job_set.started_job(file.path) + change_calls = _ChangeCallsInModule( + self.project, finder, file, call_changer) + changed_file = change_calls.get_changed_module() + if changed_file is not None: + changes.add_change(ChangeContents(file, changed_file)) + job_set.finished_job() + return changes + + def get_args(self): + """Get function arguments. + + Return a list of ``(name, default)`` tuples for all but star + and double star arguments. For arguments that don't have a + default, `None` will be used. + """ + return self._definfo().args_with_defaults + + def is_method(self): + pyfunction = self.pyname.get_object() + return isinstance(pyfunction.parent, pyobjects.PyClass) + + @utils.deprecated('Use `ChangeSignature.get_args()` instead') + def get_definition_info(self): + return self._definfo() + + def _definfo(self): + return functionutils.DefinitionInfo.read(self.pyname.get_object()) + + @utils.deprecated() + def normalize(self): + changer = _FunctionChangers( + self.pyname.get_object(), self.get_definition_info(), + [ArgumentNormalizer()]) + return self._change_calls(changer) + + @utils.deprecated() + def remove(self, index): + changer = _FunctionChangers( + self.pyname.get_object(), self.get_definition_info(), + [ArgumentRemover(index)]) + return self._change_calls(changer) + + @utils.deprecated() + def add(self, index, name, default=None, value=None): + changer = _FunctionChangers( + self.pyname.get_object(), self.get_definition_info(), + [ArgumentAdder(index, name, default, value)]) + return self._change_calls(changer) + + @utils.deprecated() + def inline_default(self, index): + changer = _FunctionChangers( + self.pyname.get_object(), self.get_definition_info(), + [ArgumentDefaultInliner(index)]) + return self._change_calls(changer) + + @utils.deprecated() + def reorder(self, new_ordering): + changer = _FunctionChangers( + self.pyname.get_object(), self.get_definition_info(), + [ArgumentReorderer(new_ordering)]) + return self._change_calls(changer) + + def get_changes(self, changers, in_hierarchy=False, resources=None, + task_handle=taskhandle.NullTaskHandle()): + """Get changes caused by this refactoring + + `changers` is a list of `_ArgumentChanger`\s. If `in_hierarchy` + is `True` the changers are applyed to all matching methods in + the class hierarchy. + `resources` can be a list of `rope.base.resource.File`\s that + should be searched for occurrences; if `None` all python files + in the project are searched. + + """ + function_changer = _FunctionChangers(self.pyname.get_object(), + self._definfo(), changers) + return self._change_calls(function_changer, in_hierarchy, + resources, task_handle) + + +class _FunctionChangers(object): + + def __init__(self, pyfunction, definition_info, changers=None): + self.pyfunction = pyfunction + self.definition_info = definition_info + self.changers = changers + self.changed_definition_infos = self._get_changed_definition_infos() + + def _get_changed_definition_infos(self): + result = [] + definition_info = self.definition_info + result.append(definition_info) + for changer in self.changers: + definition_info = copy.deepcopy(definition_info) + changer.change_definition_info(definition_info) + result.append(definition_info) + return result + + def change_definition(self, call): + return self.changed_definition_infos[-1].to_string() + + def change_call(self, primary, pyname, call): + call_info = functionutils.CallInfo.read( + primary, pyname, self.definition_info, call) + mapping = functionutils.ArgumentMapping(self.definition_info, + call_info) + + for definition_info, changer in zip(self.changed_definition_infos, + self.changers): + changer.change_argument_mapping(definition_info, mapping) + + return mapping.to_call_info( + self.changed_definition_infos[-1]).to_string() + + +class _ArgumentChanger(object): + + def change_definition_info(self, definition_info): + pass + + def change_argument_mapping(self, definition_info, argument_mapping): + pass + + +class ArgumentNormalizer(_ArgumentChanger): + pass + + +class ArgumentRemover(_ArgumentChanger): + + def __init__(self, index): + self.index = index + + def change_definition_info(self, call_info): + if self.index < len(call_info.args_with_defaults): + del call_info.args_with_defaults[self.index] + elif self.index == len(call_info.args_with_defaults) and \ + call_info.args_arg is not None: + call_info.args_arg = None + elif (self.index == len(call_info.args_with_defaults) and + call_info.args_arg is None and + call_info.keywords_arg is not None) or \ + (self.index == len(call_info.args_with_defaults) + 1 and + call_info.args_arg is not None and + call_info.keywords_arg is not None): + call_info.keywords_arg = None + + def change_argument_mapping(self, definition_info, mapping): + if self.index < len(definition_info.args_with_defaults): + name = definition_info.args_with_defaults[0] + if name in mapping.param_dict: + del mapping.param_dict[name] + + +class ArgumentAdder(_ArgumentChanger): + + def __init__(self, index, name, default=None, value=None): + self.index = index + self.name = name + self.default = default + self.value = value + + def change_definition_info(self, definition_info): + for pair in definition_info.args_with_defaults: + if pair[0] == self.name: + raise rope.base.exceptions.RefactoringError( + 'Adding duplicate parameter: <%s>.' % self.name) + definition_info.args_with_defaults.insert(self.index, + (self.name, self.default)) + + def change_argument_mapping(self, definition_info, mapping): + if self.value is not None: + mapping.param_dict[self.name] = self.value + + +class ArgumentDefaultInliner(_ArgumentChanger): + + def __init__(self, index): + self.index = index + self.remove = False + + def change_definition_info(self, definition_info): + if self.remove: + definition_info.args_with_defaults[self.index] = \ + (definition_info.args_with_defaults[self.index][0], None) + + def change_argument_mapping(self, definition_info, mapping): + default = definition_info.args_with_defaults[self.index][1] + name = definition_info.args_with_defaults[self.index][0] + if default is not None and name not in mapping.param_dict: + mapping.param_dict[name] = default + + +class ArgumentReorderer(_ArgumentChanger): + + def __init__(self, new_order, autodef=None): + """Construct an `ArgumentReorderer` + + Note that the `new_order` is a list containing the new + position of parameters; not the position each parameter + is going to be moved to. (changed in ``0.5m4``) + + For example changing ``f(a, b, c)`` to ``f(c, a, b)`` + requires passing ``[2, 0, 1]`` and *not* ``[1, 2, 0]``. + + The `autodef` (automatic default) argument, forces rope to use + it as a default if a default is needed after the change. That + happens when an argument without default is moved after + another that has a default value. Note that `autodef` should + be a string or `None`; the latter disables adding automatic + default. + + """ + self.new_order = new_order + self.autodef = autodef + + def change_definition_info(self, definition_info): + new_args = list(definition_info.args_with_defaults) + for new_index, index in enumerate(self.new_order): + new_args[new_index] = definition_info.args_with_defaults[index] + seen_default = False + for index, (arg, default) in enumerate(list(new_args)): + if default is not None: + seen_default = True + if seen_default and default is None and self.autodef is not None: + new_args[index] = (arg, self.autodef) + definition_info.args_with_defaults = new_args + + +class _ChangeCallsInModule(object): + + def __init__(self, project, occurrence_finder, resource, call_changer): + self.project = project + self.occurrence_finder = occurrence_finder + self.resource = resource + self.call_changer = call_changer + + def get_changed_module(self): + word_finder = worder.Worder(self.source) + change_collector = codeanalyze.ChangeCollector(self.source) + for occurrence in self.occurrence_finder.find_occurrences( + self.resource): + if not occurrence.is_called() and not occurrence.is_defined(): + continue + start, end = occurrence.get_primary_range() + begin_parens, end_parens = word_finder.\ + get_word_parens_range(end - 1) + if occurrence.is_called(): + primary, pyname = occurrence.get_primary_and_pyname() + changed_call = self.call_changer.change_call( + primary, pyname, self.source[start:end_parens]) + else: + changed_call = self.call_changer.change_definition( + self.source[start:end_parens]) + if changed_call is not None: + change_collector.add_change(start, end_parens, changed_call) + return change_collector.get_changed() + + @property + @utils.saveit + def pymodule(self): + return self.project.get_pymodule(self.resource) + + @property + @utils.saveit + def source(self): + if self.resource is not None: + return self.resource.read() + else: + return self.pymodule.source_code + + @property + @utils.saveit + def lines(self): + return self.pymodule.lines + + +class _MultipleFinders(object): + + def __init__(self, finders): + self.finders = finders + + def find_occurrences(self, resource=None, pymodule=None): + all_occurrences = [] + for finder in self.finders: + all_occurrences.extend(finder.find_occurrences(resource, pymodule)) + all_occurrences.sort(key=lambda x: x.get_primary_range()) + return all_occurrences + diff --git a/venv/Lib/site-packages/rope/refactor/encapsulate_field.py b/venv/Lib/site-packages/rope/refactor/encapsulate_field.py new file mode 100644 index 0000000000000000000000000000000000000000..32cb7a957b5f3a61de6dbaeda52c49fb143f78b3 --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/encapsulate_field.py @@ -0,0 +1,209 @@ +from rope.base import evaluate +from rope.base import exceptions +from rope.base import libutils +from rope.base import pynames +from rope.base import taskhandle +from rope.base import utils +from rope.base import worder +from rope.base.change import ChangeSet, ChangeContents +from rope.refactor import sourceutils, occurrences + + +class EncapsulateField(object): + + def __init__(self, project, resource, offset): + self.project = project + self.name = worder.get_name_at(resource, offset) + this_pymodule = self.project.get_pymodule(resource) + self.pyname = evaluate.eval_location(this_pymodule, offset) + if not self._is_an_attribute(self.pyname): + raise exceptions.RefactoringError( + 'Encapsulate field should be performed on class attributes.') + self.resource = self.pyname.get_definition_location()[0].get_resource() + + def get_changes(self, getter=None, setter=None, resources=None, + task_handle=taskhandle.NullTaskHandle()): + """Get the changes this refactoring makes + + If `getter` is not `None`, that will be the name of the + getter, otherwise ``get_${field_name}`` will be used. The + same is true for `setter` and if it is None set_${field_name} is + used. + + `resources` can be a list of `rope.base.resource.File`\s that + the refactoring should be applied on; if `None` all python + files in the project are searched. + + """ + if resources is None: + resources = self.project.get_python_files() + changes = ChangeSet('Encapsulate field <%s>' % self.name) + job_set = task_handle.create_jobset('Collecting Changes', + len(resources)) + if getter is None: + getter = 'get_' + self.name + if setter is None: + setter = 'set_' + self.name + renamer = GetterSetterRenameInModule( + self.project, self.name, self.pyname, getter, setter) + for file in resources: + job_set.started_job(file.path) + if file == self.resource: + result = self._change_holding_module(changes, renamer, + getter, setter) + changes.add_change(ChangeContents(self.resource, result)) + else: + result = renamer.get_changed_module(file) + if result is not None: + changes.add_change(ChangeContents(file, result)) + job_set.finished_job() + return changes + + def get_field_name(self): + """Get the name of the field to be encapsulated""" + return self.name + + def _is_an_attribute(self, pyname): + if pyname is not None and isinstance(pyname, pynames.AssignedName): + pymodule, lineno = self.pyname.get_definition_location() + scope = pymodule.get_scope().\ + get_inner_scope_for_line(lineno) + if scope.get_kind() == 'Class': + return pyname in scope.get_names().values() + parent = scope.parent + if parent is not None and parent.get_kind() == 'Class': + return pyname in parent.get_names().values() + return False + + def _get_defining_class_scope(self): + defining_scope = self._get_defining_scope() + if defining_scope.get_kind() == 'Function': + defining_scope = defining_scope.parent + return defining_scope + + def _get_defining_scope(self): + pymodule, line = self.pyname.get_definition_location() + return pymodule.get_scope().get_inner_scope_for_line(line) + + def _change_holding_module(self, changes, renamer, getter, setter): + pymodule = self.project.get_pymodule(self.resource) + class_scope = self._get_defining_class_scope() + defining_object = self._get_defining_scope().pyobject + start, end = sourceutils.get_body_region(defining_object) + + new_source = renamer.get_changed_module(pymodule=pymodule, + skip_start=start, skip_end=end) + if new_source is not None: + pymodule = libutils.get_string_module( + self.project, new_source, self.resource) + class_scope = pymodule.get_scope().\ + get_inner_scope_for_line(class_scope.get_start()) + indents = sourceutils.get_indent(self.project) * ' ' + getter = 'def %s(self):\n%sreturn self.%s' % \ + (getter, indents, self.name) + setter = 'def %s(self, value):\n%sself.%s = value' % \ + (setter, indents, self.name) + new_source = sourceutils.add_methods(pymodule, class_scope, + [getter, setter]) + return new_source + + +class GetterSetterRenameInModule(object): + + def __init__(self, project, name, pyname, getter, setter): + self.project = project + self.name = name + self.finder = occurrences.create_finder(project, name, pyname) + self.getter = getter + self.setter = setter + + def get_changed_module(self, resource=None, pymodule=None, + skip_start=0, skip_end=0): + change_finder = _FindChangesForModule(self, resource, pymodule, + skip_start, skip_end) + return change_finder.get_changed_module() + + +class _FindChangesForModule(object): + + def __init__(self, finder, resource, pymodule, skip_start, skip_end): + self.project = finder.project + self.finder = finder.finder + self.getter = finder.getter + self.setter = finder.setter + self.resource = resource + self.pymodule = pymodule + self.last_modified = 0 + self.last_set = None + self.set_index = None + self.skip_start = skip_start + self.skip_end = skip_end + + def get_changed_module(self): + result = [] + for occurrence in self.finder.find_occurrences(self.resource, + self.pymodule): + start, end = occurrence.get_word_range() + if self.skip_start <= start < self.skip_end: + continue + self._manage_writes(start, result) + result.append(self.source[self.last_modified:start]) + if self._is_assigned_in_a_tuple_assignment(occurrence): + raise exceptions.RefactoringError( + 'Cannot handle tuple assignments in encapsulate field.') + if occurrence.is_written(): + assignment_type = self.worder.get_assignment_type(start) + if assignment_type == '=': + result.append(self.setter + '(') + else: + var_name = self.source[occurrence.get_primary_range()[0]: + start] + self.getter + '()' + result.append(self.setter + '(' + var_name + + ' %s ' % assignment_type[:-1]) + current_line = self.lines.get_line_number(start) + start_line, end_line = self.pymodule.logical_lines.\ + logical_line_in(current_line) + self.last_set = self.lines.get_line_end(end_line) + end = self.source.index('=', end) + 1 + self.set_index = len(result) + else: + result.append(self.getter + '()') + self.last_modified = end + if self.last_modified != 0: + self._manage_writes(len(self.source), result) + result.append(self.source[self.last_modified:]) + return ''.join(result) + return None + + def _manage_writes(self, offset, result): + if self.last_set is not None and self.last_set <= offset: + result.append(self.source[self.last_modified:self.last_set]) + set_value = ''.join(result[self.set_index:]).strip() + del result[self.set_index:] + result.append(set_value + ')') + self.last_modified = self.last_set + self.last_set = None + + def _is_assigned_in_a_tuple_assignment(self, occurance): + offset = occurance.get_word_range()[0] + return self.worder.is_assigned_in_a_tuple_assignment(offset) + + @property + @utils.saveit + def source(self): + if self.resource is not None: + return self.resource.read() + else: + return self.pymodule.source_code + + @property + @utils.saveit + def lines(self): + if self.pymodule is None: + self.pymodule = self.project.get_pymodule(self.resource) + return self.pymodule.lines + + @property + @utils.saveit + def worder(self): + return worder.Worder(self.source) diff --git a/venv/Lib/site-packages/rope/refactor/extract.py b/venv/Lib/site-packages/rope/refactor/extract.py new file mode 100644 index 0000000000000000000000000000000000000000..bfd9a75586602fd247843eb4579e8cd08758a159 --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/extract.py @@ -0,0 +1,817 @@ +import re + +from rope.base.utils.datastructures import OrderedSet +from rope.base import ast, codeanalyze +from rope.base.change import ChangeSet, ChangeContents +from rope.base.exceptions import RefactoringError +from rope.base.utils import pycompat +from rope.refactor import (sourceutils, similarfinder, + patchedast, suites, usefunction) + + +# Extract refactoring has lots of special cases. I tried to split it +# to smaller parts to make it more manageable: +# +# _ExtractInfo: holds information about the refactoring; it is passed +# to the parts that need to have information about the refactoring +# +# _ExtractCollector: merely saves all of the information necessary for +# performing the refactoring. +# +# _DefinitionLocationFinder: finds where to insert the definition. +# +# _ExceptionalConditionChecker: checks for exceptional conditions in +# which the refactoring cannot be applied. +# +# _ExtractMethodParts: generates the pieces of code (like definition) +# needed for performing extract method. +# +# _ExtractVariableParts: like _ExtractMethodParts for variables. +# +# _ExtractPerformer: Uses above classes to collect refactoring +# changes. +# +# There are a few more helper functions and classes used by above +# classes. +class _ExtractRefactoring(object): + + def __init__(self, project, resource, start_offset, end_offset, + variable=False): + self.project = project + self.resource = resource + self.start_offset = self._fix_start(resource.read(), start_offset) + self.end_offset = self._fix_end(resource.read(), end_offset) + + def _fix_start(self, source, offset): + while offset < len(source) and source[offset].isspace(): + offset += 1 + return offset + + def _fix_end(self, source, offset): + while offset > 0 and source[offset - 1].isspace(): + offset -= 1 + return offset + + def get_changes(self, extracted_name, similar=False, global_=False): + """Get the changes this refactoring makes + + :parameters: + - `similar`: if `True`, similar expressions/statements are also + replaced. + - `global_`: if `True`, the extracted method/variable will + be global. + + """ + info = _ExtractInfo( + self.project, self.resource, self.start_offset, self.end_offset, + extracted_name, variable=self.kind == 'variable', + similar=similar, make_global=global_) + new_contents = _ExtractPerformer(info).extract() + changes = ChangeSet('Extract %s <%s>' % (self.kind, + extracted_name)) + changes.add_change(ChangeContents(self.resource, new_contents)) + return changes + + +class ExtractMethod(_ExtractRefactoring): + + def __init__(self, *args, **kwds): + super(ExtractMethod, self).__init__(*args, **kwds) + + kind = 'method' + + +class ExtractVariable(_ExtractRefactoring): + + def __init__(self, *args, **kwds): + kwds = dict(kwds) + kwds['variable'] = True + super(ExtractVariable, self).__init__(*args, **kwds) + + kind = 'variable' + + +class _ExtractInfo(object): + """Holds information about the extract to be performed""" + + def __init__(self, project, resource, start, end, new_name, + variable, similar, make_global): + self.project = project + self.resource = resource + self.pymodule = project.get_pymodule(resource) + self.global_scope = self.pymodule.get_scope() + self.source = self.pymodule.source_code + self.lines = self.pymodule.lines + self.new_name = new_name + self.variable = variable + self.similar = similar + self._init_parts(start, end) + self._init_scope() + self.make_global = make_global + + def _init_parts(self, start, end): + self.region = (self._choose_closest_line_end(start), + self._choose_closest_line_end(end, end=True)) + + start = self.logical_lines.logical_line_in( + self.lines.get_line_number(self.region[0]))[0] + end = self.logical_lines.logical_line_in( + self.lines.get_line_number(self.region[1]))[1] + self.region_lines = (start, end) + + self.lines_region = (self.lines.get_line_start(self.region_lines[0]), + self.lines.get_line_end(self.region_lines[1])) + + @property + def logical_lines(self): + return self.pymodule.logical_lines + + def _init_scope(self): + start_line = self.region_lines[0] + scope = self.global_scope.get_inner_scope_for_line(start_line) + if scope.get_kind() != 'Module' and scope.get_start() == start_line: + scope = scope.parent + self.scope = scope + self.scope_region = self._get_scope_region(self.scope) + + def _get_scope_region(self, scope): + return (self.lines.get_line_start(scope.get_start()), + self.lines.get_line_end(scope.get_end()) + 1) + + def _choose_closest_line_end(self, offset, end=False): + lineno = self.lines.get_line_number(offset) + line_start = self.lines.get_line_start(lineno) + line_end = self.lines.get_line_end(lineno) + if self.source[line_start:offset].strip() == '': + if end: + return line_start - 1 + else: + return line_start + elif self.source[offset:line_end].strip() == '': + return min(line_end, len(self.source)) + return offset + + @property + def one_line(self): + return self.region != self.lines_region and \ + (self.logical_lines.logical_line_in(self.region_lines[0]) == + self.logical_lines.logical_line_in(self.region_lines[1])) + + @property + def global_(self): + return self.scope.parent is None + + @property + def method(self): + return self.scope.parent is not None and \ + self.scope.parent.get_kind() == 'Class' + + @property + def indents(self): + return sourceutils.get_indents(self.pymodule.lines, + self.region_lines[0]) + + @property + def scope_indents(self): + if self.global_: + return 0 + return sourceutils.get_indents(self.pymodule.lines, + self.scope.get_start()) + + @property + def extracted(self): + return self.source[self.region[0]:self.region[1]] + + _returned = None + + @property + def returned(self): + """Does the extracted piece contain return statement""" + if self._returned is None: + node = _parse_text(self.extracted) + self._returned = usefunction._returns_last(node) + return self._returned + + +class _ExtractCollector(object): + """Collects information needed for performing the extract""" + + def __init__(self, info): + self.definition = None + self.body_pattern = None + self.checks = {} + self.replacement_pattern = None + self.matches = None + self.replacements = None + self.definition_location = None + + +class _ExtractPerformer(object): + + def __init__(self, info): + self.info = info + _ExceptionalConditionChecker()(self.info) + + def extract(self): + extract_info = self._collect_info() + content = codeanalyze.ChangeCollector(self.info.source) + definition = extract_info.definition + lineno, indents = extract_info.definition_location + offset = self.info.lines.get_line_start(lineno) + indented = sourceutils.fix_indentation(definition, indents) + content.add_change(offset, offset, indented) + self._replace_occurrences(content, extract_info) + return content.get_changed() + + def _replace_occurrences(self, content, extract_info): + for match in extract_info.matches: + replacement = similarfinder.CodeTemplate( + extract_info.replacement_pattern) + mapping = {} + for name in replacement.get_names(): + node = match.get_ast(name) + if node: + start, end = patchedast.node_region(match.get_ast(name)) + mapping[name] = self.info.source[start:end] + else: + mapping[name] = name + region = match.get_region() + content.add_change(region[0], region[1], + replacement.substitute(mapping)) + + def _collect_info(self): + extract_collector = _ExtractCollector(self.info) + self._find_definition(extract_collector) + self._find_matches(extract_collector) + self._find_definition_location(extract_collector) + return extract_collector + + def _find_matches(self, collector): + regions = self._where_to_search() + finder = similarfinder.SimilarFinder(self.info.pymodule) + matches = [] + for start, end in regions: + region_matches = finder.get_matches(collector.body_pattern, + collector.checks, start, end) + # Don't extract overlapping regions + last_match_end = -1 + for region_match in region_matches: + start, end = region_match.get_region() + if last_match_end < start: + matches.append(region_match) + last_match_end = end + collector.matches = matches + + def _where_to_search(self): + if self.info.similar: + if self.info.make_global or self.info.global_: + return [(0, len(self.info.pymodule.source_code))] + if self.info.method and not self.info.variable: + class_scope = self.info.scope.parent + regions = [] + method_kind = _get_function_kind(self.info.scope) + for scope in class_scope.get_scopes(): + if method_kind == 'method' and \ + _get_function_kind(scope) != 'method': + continue + start = self.info.lines.get_line_start(scope.get_start()) + end = self.info.lines.get_line_end(scope.get_end()) + regions.append((start, end)) + return regions + else: + if self.info.variable: + return [self.info.scope_region] + else: + return [self.info._get_scope_region( + self.info.scope.parent)] + else: + return [self.info.region] + + def _find_definition_location(self, collector): + matched_lines = [] + for match in collector.matches: + start = self.info.lines.get_line_number(match.get_region()[0]) + start_line = self.info.logical_lines.logical_line_in(start)[0] + matched_lines.append(start_line) + location_finder = _DefinitionLocationFinder(self.info, matched_lines) + collector.definition_location = (location_finder.find_lineno(), + location_finder.find_indents()) + + def _find_definition(self, collector): + if self.info.variable: + parts = _ExtractVariableParts(self.info) + else: + parts = _ExtractMethodParts(self.info) + collector.definition = parts.get_definition() + collector.body_pattern = parts.get_body_pattern() + collector.replacement_pattern = parts.get_replacement_pattern() + collector.checks = parts.get_checks() + + +class _DefinitionLocationFinder(object): + + def __init__(self, info, matched_lines): + self.info = info + self.matched_lines = matched_lines + # This only happens when subexpressions cannot be matched + if not matched_lines: + self.matched_lines.append(self.info.region_lines[0]) + + def find_lineno(self): + if self.info.variable and not self.info.make_global: + return self._get_before_line() + if self.info.make_global or self.info.global_: + toplevel = self._find_toplevel(self.info.scope) + ast = self.info.pymodule.get_ast() + newlines = sorted(self.matched_lines + [toplevel.get_end() + 1]) + return suites.find_visible(ast, newlines) + return self._get_after_scope() + + def _find_toplevel(self, scope): + toplevel = scope + if toplevel.parent is not None: + while toplevel.parent.parent is not None: + toplevel = toplevel.parent + return toplevel + + def find_indents(self): + if self.info.variable and not self.info.make_global: + return sourceutils.get_indents(self.info.lines, + self._get_before_line()) + else: + if self.info.global_ or self.info.make_global: + return 0 + return self.info.scope_indents + + def _get_before_line(self): + ast = self.info.scope.pyobject.get_ast() + return suites.find_visible(ast, self.matched_lines) + + def _get_after_scope(self): + return self.info.scope.get_end() + 1 + + +class _ExceptionalConditionChecker(object): + + def __call__(self, info): + self.base_conditions(info) + if info.one_line: + self.one_line_conditions(info) + else: + self.multi_line_conditions(info) + + def base_conditions(self, info): + if info.region[1] > info.scope_region[1]: + raise RefactoringError('Bad region selected for extract method') + end_line = info.region_lines[1] + end_scope = info.global_scope.get_inner_scope_for_line(end_line) + if end_scope != info.scope and end_scope.get_end() != end_line: + raise RefactoringError('Bad region selected for extract method') + try: + extracted = info.source[info.region[0]:info.region[1]] + if info.one_line: + extracted = '(%s)' % extracted + if _UnmatchedBreakOrContinueFinder.has_errors(extracted): + raise RefactoringError('A break/continue without having a ' + 'matching for/while loop.') + except SyntaxError: + raise RefactoringError('Extracted piece should ' + 'contain complete statements.') + + def one_line_conditions(self, info): + if self._is_region_on_a_word(info): + raise RefactoringError('Should extract complete statements.') + if info.variable and not info.one_line: + raise RefactoringError('Extract variable should not ' + 'span multiple lines.') + + def multi_line_conditions(self, info): + node = _parse_text(info.source[info.region[0]:info.region[1]]) + count = usefunction._return_count(node) + if count > 1: + raise RefactoringError('Extracted piece can have only one ' + 'return statement.') + if usefunction._yield_count(node): + raise RefactoringError('Extracted piece cannot ' + 'have yield statements.') + if count == 1 and not usefunction._returns_last(node): + raise RefactoringError('Return should be the last statement.') + if info.region != info.lines_region: + raise RefactoringError('Extracted piece should ' + 'contain complete statements.') + + def _is_region_on_a_word(self, info): + if info.region[0] > 0 and \ + self._is_on_a_word(info, info.region[0] - 1) or \ + self._is_on_a_word(info, info.region[1] - 1): + return True + + def _is_on_a_word(self, info, offset): + prev = info.source[offset] + if not (prev.isalnum() or prev == '_') or \ + offset + 1 == len(info.source): + return False + next = info.source[offset + 1] + return next.isalnum() or next == '_' + + +class _ExtractMethodParts(object): + + def __init__(self, info): + self.info = info + self.info_collector = self._create_info_collector() + + def get_definition(self): + if self.info.global_: + return '\n%s\n' % self._get_function_definition() + else: + return '\n%s' % self._get_function_definition() + + def get_replacement_pattern(self): + variables = [] + variables.extend(self._find_function_arguments()) + variables.extend(self._find_function_returns()) + return similarfinder.make_pattern(self._get_call(), variables) + + def get_body_pattern(self): + variables = [] + variables.extend(self._find_function_arguments()) + variables.extend(self._find_function_returns()) + variables.extend(self._find_temps()) + return similarfinder.make_pattern(self._get_body(), variables) + + def _get_body(self): + result = sourceutils.fix_indentation(self.info.extracted, 0) + if self.info.one_line: + result = '(%s)' % result + return result + + def _find_temps(self): + return usefunction.find_temps(self.info.project, + self._get_body()) + + def get_checks(self): + if self.info.method and not self.info.make_global: + if _get_function_kind(self.info.scope) == 'method': + class_name = similarfinder._pydefined_to_str( + self.info.scope.parent.pyobject) + return {self._get_self_name(): 'type=' + class_name} + return {} + + def _create_info_collector(self): + zero = self.info.scope.get_start() - 1 + start_line = self.info.region_lines[0] - zero + end_line = self.info.region_lines[1] - zero + info_collector = _FunctionInformationCollector(start_line, end_line, + self.info.global_) + body = self.info.source[self.info.scope_region[0]: + self.info.scope_region[1]] + node = _parse_text(body) + ast.walk(node, info_collector) + return info_collector + + def _get_function_definition(self): + args = self._find_function_arguments() + returns = self._find_function_returns() + result = [] + if self.info.method and not self.info.make_global and \ + _get_function_kind(self.info.scope) != 'method': + result.append('@staticmethod\n') + result.append('def %s:\n' % self._get_function_signature(args)) + unindented_body = self._get_unindented_function_body(returns) + indents = sourceutils.get_indent(self.info.project) + function_body = sourceutils.indent_lines(unindented_body, indents) + result.append(function_body) + definition = ''.join(result) + + return definition + '\n' + + def _get_function_signature(self, args): + args = list(args) + prefix = '' + if self._extracting_method(): + self_name = self._get_self_name() + if self_name is None: + raise RefactoringError('Extracting a method from a function ' + 'with no self argument.') + if self_name in args: + args.remove(self_name) + args.insert(0, self_name) + return prefix + self.info.new_name + \ + '(%s)' % self._get_comma_form(args) + + def _extracting_method(self): + return self.info.method and not self.info.make_global and \ + _get_function_kind(self.info.scope) == 'method' + + def _get_self_name(self): + param_names = self.info.scope.pyobject.get_param_names() + if param_names: + return param_names[0] + + def _get_function_call(self, args): + prefix = '' + if self.info.method and not self.info.make_global: + if _get_function_kind(self.info.scope) == 'method': + self_name = self._get_self_name() + if self_name in args: + args.remove(self_name) + prefix = self_name + '.' + else: + prefix = self.info.scope.parent.pyobject.get_name() + '.' + return prefix + '%s(%s)' % (self.info.new_name, + self._get_comma_form(args)) + + def _get_comma_form(self, names): + result = '' + if names: + result += names[0] + for name in names[1:]: + result += ', ' + name + return result + + def _get_call(self): + if self.info.one_line: + args = self._find_function_arguments() + return self._get_function_call(args) + args = self._find_function_arguments() + returns = self._find_function_returns() + call_prefix = '' + if returns: + call_prefix = self._get_comma_form(returns) + ' = ' + if self.info.returned: + call_prefix = 'return ' + return call_prefix + self._get_function_call(args) + + def _find_function_arguments(self): + # if not make_global, do not pass any global names; they are + # all visible. + if self.info.global_ and not self.info.make_global: + return () + if not self.info.one_line: + result = (self.info_collector.prewritten & + self.info_collector.read) + result |= (self.info_collector.prewritten & + self.info_collector.postread & + (self.info_collector.maybe_written - + self.info_collector.written)) + return list(result) + start = self.info.region[0] + if start == self.info.lines_region[0]: + start = start + re.search('\S', self.info.extracted).start() + function_definition = self.info.source[start:self.info.region[1]] + read = _VariableReadsAndWritesFinder.find_reads_for_one_liners( + function_definition) + return list(self.info_collector.prewritten.intersection(read)) + + def _find_function_returns(self): + if self.info.one_line or self.info.returned: + return [] + written = self.info_collector.written | \ + self.info_collector.maybe_written + return list(written & self.info_collector.postread) + + def _get_unindented_function_body(self, returns): + if self.info.one_line: + return 'return ' + _join_lines(self.info.extracted) + extracted_body = self.info.extracted + unindented_body = sourceutils.fix_indentation(extracted_body, 0) + if returns: + unindented_body += '\nreturn %s' % self._get_comma_form(returns) + return unindented_body + + +class _ExtractVariableParts(object): + + def __init__(self, info): + self.info = info + + def get_definition(self): + result = self.info.new_name + ' = ' + \ + _join_lines(self.info.extracted) + '\n' + return result + + def get_body_pattern(self): + return '(%s)' % self.info.extracted.strip() + + def get_replacement_pattern(self): + return self.info.new_name + + def get_checks(self): + return {} + + +class _FunctionInformationCollector(object): + + def __init__(self, start, end, is_global): + self.start = start + self.end = end + self.is_global = is_global + self.prewritten = OrderedSet() + self.maybe_written = OrderedSet() + self.written = OrderedSet() + self.read = OrderedSet() + self.postread = OrderedSet() + self.postwritten = OrderedSet() + self.host_function = True + self.conditional = False + + def _read_variable(self, name, lineno): + if self.start <= lineno <= self.end: + if name not in self.written: + if not self.conditional or name not in self.maybe_written: + self.read.add(name) + if self.end < lineno: + if name not in self.postwritten: + self.postread.add(name) + + def _written_variable(self, name, lineno): + if self.start <= lineno <= self.end: + if self.conditional: + self.maybe_written.add(name) + else: + self.written.add(name) + if self.start > lineno: + self.prewritten.add(name) + if self.end < lineno: + self.postwritten.add(name) + + def _FunctionDef(self, node): + if not self.is_global and self.host_function: + self.host_function = False + for name in _get_argnames(node.args): + self._written_variable(name, node.lineno) + for child in node.body: + ast.walk(child, self) + else: + self._written_variable(node.name, node.lineno) + visitor = _VariableReadsAndWritesFinder() + for child in node.body: + ast.walk(child, visitor) + for name in visitor.read - visitor.written: + self._read_variable(name, node.lineno) + + def _Name(self, node): + if isinstance(node.ctx, (ast.Store, ast.AugStore)): + self._written_variable(node.id, node.lineno) + if not isinstance(node.ctx, ast.Store): + self._read_variable(node.id, node.lineno) + + def _Assign(self, node): + ast.walk(node.value, self) + for child in node.targets: + ast.walk(child, self) + + def _ClassDef(self, node): + self._written_variable(node.name, node.lineno) + + def _handle_conditional_node(self, node): + self.conditional = True + try: + for child in ast.get_child_nodes(node): + ast.walk(child, self) + finally: + self.conditional = False + + def _If(self, node): + self._handle_conditional_node(node) + + def _While(self, node): + self._handle_conditional_node(node) + + def _For(self, node): + self.conditional = True + try: + # iter has to be checked before the target variables + ast.walk(node.iter, self) + ast.walk(node.target, self) + + for child in node.body: + ast.walk(child, self) + for child in node.orelse: + ast.walk(child, self) + finally: + self.conditional = False + + +def _get_argnames(arguments): + result = [pycompat.get_ast_arg_arg(node) for node in arguments.args + if isinstance(node, pycompat.ast_arg_type)] + if arguments.vararg: + result.append(pycompat.get_ast_arg_arg(arguments.vararg)) + if arguments.kwarg: + result.append(pycompat.get_ast_arg_arg(arguments.kwarg)) + return result + + +class _VariableReadsAndWritesFinder(object): + + def __init__(self): + self.written = set() + self.read = set() + + def _Name(self, node): + if isinstance(node.ctx, (ast.Store, ast.AugStore)): + self.written.add(node.id) + if not isinstance(node, ast.Store): + self.read.add(node.id) + + def _FunctionDef(self, node): + self.written.add(node.name) + visitor = _VariableReadsAndWritesFinder() + for child in ast.get_child_nodes(node): + ast.walk(child, visitor) + self.read.update(visitor.read - visitor.written) + + def _Class(self, node): + self.written.add(node.name) + + @staticmethod + def find_reads_and_writes(code): + if code.strip() == '': + return set(), set() + if isinstance(code, unicode): + code = code.encode('utf-8') + node = _parse_text(code) + visitor = _VariableReadsAndWritesFinder() + ast.walk(node, visitor) + return visitor.read, visitor.written + + @staticmethod + def find_reads_for_one_liners(code): + if code.strip() == '': + return set(), set() + node = _parse_text(code) + visitor = _VariableReadsAndWritesFinder() + ast.walk(node, visitor) + return visitor.read + + +class _UnmatchedBreakOrContinueFinder(object): + + def __init__(self): + self.error = False + self.loop_count = 0 + + def _For(self, node): + self.loop_encountered(node) + + def _While(self, node): + self.loop_encountered(node) + + def loop_encountered(self, node): + self.loop_count += 1 + for child in node.body: + ast.walk(child, self) + self.loop_count -= 1 + if node.orelse: + if isinstance(node.orelse,(list,tuple)): + for node_ in node.orelse: + ast.walk(node_, self) + else: + ast.walk(node.orelse, self) + + def _Break(self, node): + self.check_loop() + + def _Continue(self, node): + self.check_loop() + + def check_loop(self): + if self.loop_count < 1: + self.error = True + + def _FunctionDef(self, node): + pass + + def _ClassDef(self, node): + pass + + @staticmethod + def has_errors(code): + if code.strip() == '': + return False + node = _parse_text(code) + visitor = _UnmatchedBreakOrContinueFinder() + ast.walk(node, visitor) + return visitor.error + + +def _get_function_kind(scope): + return scope.pyobject.get_kind() + + +def _parse_text(body): + body = sourceutils.fix_indentation(body, 0) + node = ast.parse(body) + return node + + +def _join_lines(code): + lines = [] + for line in code.splitlines(): + if line.endswith('\\'): + lines.append(line[:-1].strip()) + else: + lines.append(line.strip()) + return ' '.join(lines) diff --git a/venv/Lib/site-packages/rope/refactor/functionutils.py b/venv/Lib/site-packages/rope/refactor/functionutils.py new file mode 100644 index 0000000000000000000000000000000000000000..58baf9174fd9bec2f18849063f832847103cfdea --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/functionutils.py @@ -0,0 +1,222 @@ +import rope.base.exceptions +import rope.base.pyobjects +from rope.base.builtins import Lambda +from rope.base import worder + + +class DefinitionInfo(object): + + def __init__(self, function_name, is_method, args_with_defaults, + args_arg, keywords_arg): + self.function_name = function_name + self.is_method = is_method + self.args_with_defaults = args_with_defaults + self.args_arg = args_arg + self.keywords_arg = keywords_arg + + def to_string(self): + return '%s(%s)' % (self.function_name, self.arguments_to_string()) + + def arguments_to_string(self, from_index=0): + params = [] + for arg, default in self.args_with_defaults: + if default is not None: + params.append('%s=%s' % (arg, default)) + else: + params.append(arg) + if self.args_arg is not None: + params.append('*' + self.args_arg) + if self.keywords_arg: + params.append('**' + self.keywords_arg) + return ', '.join(params[from_index:]) + + @staticmethod + def _read(pyfunction, code): + kind = pyfunction.get_kind() + is_method = kind == 'method' + is_lambda = kind == 'lambda' + info = _FunctionParser(code, is_method, is_lambda) + args, keywords = info.get_parameters() + args_arg = None + keywords_arg = None + if args and args[-1].startswith('**'): + keywords_arg = args[-1][2:] + del args[-1] + if args and args[-1].startswith('*'): + args_arg = args[-1][1:] + del args[-1] + args_with_defaults = [(name, None) for name in args] + args_with_defaults.extend(keywords) + return DefinitionInfo(info.get_function_name(), is_method, + args_with_defaults, args_arg, keywords_arg) + + @staticmethod + def read(pyfunction): + pymodule = pyfunction.get_module() + word_finder = worder.Worder(pymodule.source_code) + lineno = pyfunction.get_ast().lineno + start = pymodule.lines.get_line_start(lineno) + if isinstance(pyfunction, Lambda): + call = word_finder.get_lambda_and_args(start) + else: + call = word_finder.get_function_and_args_in_header(start) + return DefinitionInfo._read(pyfunction, call) + + +class CallInfo(object): + + def __init__(self, function_name, args, keywords, args_arg, + keywords_arg, implicit_arg, constructor): + self.function_name = function_name + self.args = args + self.keywords = keywords + self.args_arg = args_arg + self.keywords_arg = keywords_arg + self.implicit_arg = implicit_arg + self.constructor = constructor + + def to_string(self): + function = self.function_name + if self.implicit_arg: + function = self.args[0] + '.' + self.function_name + params = [] + start = 0 + if self.implicit_arg or self.constructor: + start = 1 + if self.args[start:]: + params.extend(self.args[start:]) + if self.keywords: + params.extend(['%s=%s' % (name, value) + for name, value in self.keywords]) + if self.args_arg is not None: + params.append('*' + self.args_arg) + if self.keywords_arg: + params.append('**' + self.keywords_arg) + return '%s(%s)' % (function, ', '.join(params)) + + @staticmethod + def read(primary, pyname, definition_info, code): + is_method_call = CallInfo._is_method_call(primary, pyname) + is_constructor = CallInfo._is_class(pyname) + is_classmethod = CallInfo._is_classmethod(pyname) + info = _FunctionParser(code, is_method_call or is_classmethod) + args, keywords = info.get_parameters() + args_arg = None + keywords_arg = None + if args and args[-1].startswith('**'): + keywords_arg = args[-1][2:] + del args[-1] + if args and args[-1].startswith('*'): + args_arg = args[-1][1:] + del args[-1] + if is_constructor: + args.insert(0, definition_info.args_with_defaults[0][0]) + return CallInfo(info.get_function_name(), args, keywords, args_arg, + keywords_arg, is_method_call or is_classmethod, + is_constructor) + + @staticmethod + def _is_method_call(primary, pyname): + return primary is not None and \ + isinstance(primary.get_object().get_type(), + rope.base.pyobjects.PyClass) and \ + CallInfo._is_method(pyname) + + @staticmethod + def _is_class(pyname): + return pyname is not None and \ + isinstance(pyname.get_object(), + rope.base.pyobjects.PyClass) + + @staticmethod + def _is_method(pyname): + if pyname is not None and \ + isinstance(pyname.get_object(), rope.base.pyobjects.PyFunction): + return pyname.get_object().get_kind() == 'method' + return False + + @staticmethod + def _is_classmethod(pyname): + if pyname is not None and \ + isinstance(pyname.get_object(), rope.base.pyobjects.PyFunction): + return pyname.get_object().get_kind() == 'classmethod' + return False + + +class ArgumentMapping(object): + + def __init__(self, definition_info, call_info): + self.call_info = call_info + self.param_dict = {} + self.keyword_args = [] + self.args_arg = [] + for index, value in enumerate(call_info.args): + if index < len(definition_info.args_with_defaults): + name = definition_info.args_with_defaults[index][0] + self.param_dict[name] = value + else: + self.args_arg.append(value) + for name, value in call_info.keywords: + index = -1 + for pair in definition_info.args_with_defaults: + if pair[0] == name: + self.param_dict[name] = value + break + else: + self.keyword_args.append((name, value)) + + def to_call_info(self, definition_info): + args = [] + keywords = [] + for index in range(len(definition_info.args_with_defaults)): + name = definition_info.args_with_defaults[index][0] + if name in self.param_dict: + args.append(self.param_dict[name]) + else: + for i in range(index, len(definition_info.args_with_defaults)): + name = definition_info.args_with_defaults[i][0] + if name in self.param_dict: + keywords.append((name, self.param_dict[name])) + break + args.extend(self.args_arg) + keywords.extend(self.keyword_args) + return CallInfo(self.call_info.function_name, args, keywords, + self.call_info.args_arg, self.call_info.keywords_arg, + self.call_info.implicit_arg, + self.call_info.constructor) + + +class _FunctionParser(object): + + def __init__(self, call, implicit_arg, is_lambda=False): + self.call = call + self.implicit_arg = implicit_arg + self.word_finder = worder.Worder(self.call) + if is_lambda: + self.last_parens = self.call.rindex(':') + else: + self.last_parens = self.call.rindex(')') + self.first_parens = self.word_finder._find_parens_start( + self.last_parens) + + def get_parameters(self): + args, keywords = self.word_finder.get_parameters(self.first_parens, + self.last_parens) + if self.is_called_as_a_method(): + instance = self.call[:self.call.rindex('.', 0, self.first_parens)] + args.insert(0, instance.strip()) + return args, keywords + + def get_instance(self): + if self.is_called_as_a_method(): + return self.word_finder.get_primary_at( + self.call.rindex('.', 0, self.first_parens) - 1) + + def get_function_name(self): + if self.is_called_as_a_method(): + return self.word_finder.get_word_at(self.first_parens - 1) + else: + return self.word_finder.get_primary_at(self.first_parens - 1) + + def is_called_as_a_method(self): + return self.implicit_arg and '.' in self.call[:self.first_parens] diff --git a/venv/Lib/site-packages/rope/refactor/importutils/__init__.py b/venv/Lib/site-packages/rope/refactor/importutils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..6a44f01b6cd90891465a3101b9184130d3635bc8 --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/importutils/__init__.py @@ -0,0 +1,316 @@ +"""A package for handling imports + +This package provides tools for modifying module imports after +refactorings or as a separate task. + +""" +import rope.base.evaluate +from rope.base import libutils +from rope.base.change import ChangeSet, ChangeContents +from rope.refactor import occurrences, rename +from rope.refactor.importutils import module_imports, actions +from rope.refactor.importutils.importinfo import NormalImport, FromImport +import rope.base.codeanalyze + + +class ImportOrganizer(object): + """Perform some import-related commands + + Each method returns a `rope.base.change.Change` object. + + """ + + def __init__(self, project): + self.project = project + self.import_tools = ImportTools(self.project) + + def organize_imports(self, resource, offset=None): + return self._perform_command_on_import_tools( + self.import_tools.organize_imports, resource, offset) + + def expand_star_imports(self, resource, offset=None): + return self._perform_command_on_import_tools( + self.import_tools.expand_stars, resource, offset) + + def froms_to_imports(self, resource, offset=None): + return self._perform_command_on_import_tools( + self.import_tools.froms_to_imports, resource, offset) + + def relatives_to_absolutes(self, resource, offset=None): + return self._perform_command_on_import_tools( + self.import_tools.relatives_to_absolutes, resource, offset) + + def handle_long_imports(self, resource, offset=None): + return self._perform_command_on_import_tools( + self.import_tools.handle_long_imports, resource, offset) + + def _perform_command_on_import_tools(self, method, resource, offset): + pymodule = self.project.get_pymodule(resource) + before_performing = pymodule.source_code + import_filter = None + if offset is not None: + import_filter = self._line_filter( + pymodule.lines.get_line_number(offset)) + result = method(pymodule, import_filter=import_filter) + if result is not None and result != before_performing: + changes = ChangeSet(method.__name__.replace('_', ' ') + + ' in <%s>' % resource.path) + changes.add_change(ChangeContents(resource, result)) + return changes + + def _line_filter(self, lineno): + def import_filter(import_stmt): + return import_stmt.start_line <= lineno < import_stmt.end_line + return import_filter + + +class ImportTools(object): + + def __init__(self, project): + self.project = project + + def get_import(self, resource): + """The import statement for `resource`""" + module_name = libutils.modname(resource) + return NormalImport(((module_name, None), )) + + def get_from_import(self, resource, name): + """The from import statement for `name` in `resource`""" + module_name = libutils.modname(resource) + names = [] + if isinstance(name, list): + names = [(imported, None) for imported in name] + else: + names = [(name, None), ] + return FromImport(module_name, 0, tuple(names)) + + def module_imports(self, module, imports_filter=None): + return module_imports.ModuleImports(self.project, module, + imports_filter) + + def froms_to_imports(self, pymodule, import_filter=None): + pymodule = self._clean_up_imports(pymodule, import_filter) + module_imports = self.module_imports(pymodule, import_filter) + for import_stmt in module_imports.imports: + if import_stmt.readonly or \ + not self._is_transformable_to_normal(import_stmt.import_info): + continue + pymodule = self._from_to_normal(pymodule, import_stmt) + + # Adding normal imports in place of froms + module_imports = self.module_imports(pymodule, import_filter) + for import_stmt in module_imports.imports: + if not import_stmt.readonly and \ + self._is_transformable_to_normal(import_stmt.import_info): + import_stmt.import_info = \ + NormalImport(((import_stmt.import_info.module_name, + None),)) + module_imports.remove_duplicates() + return module_imports.get_changed_source() + + def expand_stars(self, pymodule, import_filter=None): + module_imports = self.module_imports(pymodule, import_filter) + module_imports.expand_stars() + return module_imports.get_changed_source() + + def _from_to_normal(self, pymodule, import_stmt): + resource = pymodule.get_resource() + from_import = import_stmt.import_info + module_name = from_import.module_name + for name, alias in from_import.names_and_aliases: + imported = name + if alias is not None: + imported = alias + occurrence_finder = occurrences.create_finder( + self.project, imported, pymodule[imported], imports=False) + source = rename.rename_in_module( + occurrence_finder, module_name + '.' + name, + pymodule=pymodule, replace_primary=True) + if source is not None: + pymodule = libutils.get_string_module( + self.project, source, resource) + return pymodule + + def _clean_up_imports(self, pymodule, import_filter): + resource = pymodule.get_resource() + module_with_imports = self.module_imports(pymodule, import_filter) + module_with_imports.expand_stars() + source = module_with_imports.get_changed_source() + if source is not None: + pymodule = libutils.get_string_module( + self.project, source, resource) + source = self.relatives_to_absolutes(pymodule) + if source is not None: + pymodule = libutils.get_string_module( + self.project, source, resource) + + module_with_imports = self.module_imports(pymodule, import_filter) + module_with_imports.remove_duplicates() + module_with_imports.remove_unused_imports() + source = module_with_imports.get_changed_source() + if source is not None: + pymodule = libutils.get_string_module( + self.project, source, resource) + return pymodule + + def relatives_to_absolutes(self, pymodule, import_filter=None): + module_imports = self.module_imports(pymodule, import_filter) + to_be_absolute_list = module_imports.get_relative_to_absolute_list() + for name, absolute_name in to_be_absolute_list: + pymodule = self._rename_in_module(pymodule, name, absolute_name) + module_imports = self.module_imports(pymodule, import_filter) + module_imports.get_relative_to_absolute_list() + source = module_imports.get_changed_source() + if source is None: + source = pymodule.source_code + return source + + def _is_transformable_to_normal(self, import_info): + if not isinstance(import_info, FromImport): + return False + return True + + def organize_imports(self, pymodule, + unused=True, duplicates=True, + selfs=True, sort=True, import_filter=None): + if unused or duplicates: + module_imports = self.module_imports(pymodule, import_filter) + if unused: + module_imports.remove_unused_imports() + if self.project.prefs.get("split_imports"): + module_imports.force_single_imports() + if duplicates: + module_imports.remove_duplicates() + source = module_imports.get_changed_source() + if source is not None: + pymodule = libutils.get_string_module( + self.project, source, pymodule.get_resource()) + if selfs: + pymodule = self._remove_self_imports(pymodule, import_filter) + if sort: + return self.sort_imports(pymodule, import_filter) + else: + return pymodule.source_code + + def _remove_self_imports(self, pymodule, import_filter=None): + module_imports = self.module_imports(pymodule, import_filter) + to_be_fixed, to_be_renamed = \ + module_imports.get_self_import_fix_and_rename_list() + for name in to_be_fixed: + try: + pymodule = self._rename_in_module(pymodule, name, '', + till_dot=True) + except ValueError: + # There is a self import with direct access to it + return pymodule + for name, new_name in to_be_renamed: + pymodule = self._rename_in_module(pymodule, name, new_name) + module_imports = self.module_imports(pymodule, import_filter) + module_imports.get_self_import_fix_and_rename_list() + source = module_imports.get_changed_source() + if source is not None: + pymodule = libutils.get_string_module( + self.project, source, pymodule.get_resource()) + return pymodule + + def _rename_in_module(self, pymodule, name, new_name, till_dot=False): + old_name = name.split('.')[-1] + old_pyname = rope.base.evaluate.eval_str(pymodule.get_scope(), name) + occurrence_finder = occurrences.create_finder( + self.project, old_name, old_pyname, imports=False) + changes = rope.base.codeanalyze.ChangeCollector(pymodule.source_code) + for occurrence in occurrence_finder.find_occurrences( + pymodule=pymodule): + start, end = occurrence.get_primary_range() + if till_dot: + new_end = pymodule.source_code.index('.', end) + 1 + space = pymodule.source_code[end:new_end - 1].strip() + if not space == '': + for c in space: + if not c.isspace() and c not in '\\': + raise ValueError() + end = new_end + changes.add_change(start, end, new_name) + source = changes.get_changed() + if source is not None: + pymodule = libutils.get_string_module( + self.project, source, pymodule.get_resource()) + return pymodule + + def sort_imports(self, pymodule, import_filter=None): + module_imports = self.module_imports(pymodule, import_filter) + module_imports.sort_imports() + return module_imports.get_changed_source() + + def handle_long_imports(self, pymodule, maxdots=2, maxlength=27, + import_filter=None): + # IDEA: `maxdots` and `maxlength` can be specified in project config + # adding new from imports + module_imports = self.module_imports(pymodule, import_filter) + to_be_fixed = module_imports.handle_long_imports(maxdots, maxlength) + # performing the renaming + pymodule = libutils.get_string_module( + self.project, module_imports.get_changed_source(), + resource=pymodule.get_resource()) + for name in to_be_fixed: + pymodule = self._rename_in_module(pymodule, name, + name.split('.')[-1]) + # organizing imports + return self.organize_imports(pymodule, selfs=False, sort=False, + import_filter=import_filter) + + +def get_imports(project, pydefined): + """A shortcut for getting the `ImportInfo`\s used in a scope""" + pymodule = pydefined.get_module() + module = module_imports.ModuleImports(project, pymodule) + if pymodule == pydefined: + return [stmt.import_info for stmt in module.imports] + return module.get_used_imports(pydefined) + + +def get_module_imports(project, pymodule): + """A shortcut for creating a `module_imports.ModuleImports` object""" + return module_imports.ModuleImports(project, pymodule) + + +def add_import(project, pymodule, module_name, name=None): + imports = get_module_imports(project, pymodule) + candidates = [] + names = [] + selected_import = None + # from mod import name + if name is not None: + from_import = FromImport(module_name, 0, [(name, None)]) + names.append(name) + candidates.append(from_import) + # from pkg import mod + if '.' in module_name: + pkg, mod = module_name.rsplit('.', 1) + from_import = FromImport(pkg, 0, [(mod, None)]) + if project.prefs.get('prefer_module_from_imports'): + selected_import = from_import + candidates.append(from_import) + if name: + names.append(mod + '.' + name) + else: + names.append(mod) + # import mod + normal_import = NormalImport([(module_name, None)]) + if name: + names.append(module_name + '.' + name) + else: + names.append(module_name) + + candidates.append(normal_import) + + visitor = actions.AddingVisitor(project, candidates) + if selected_import is None: + selected_import = normal_import + for import_statement in imports.imports: + if import_statement.accept(visitor): + selected_import = visitor.import_info + break + imports.add_import(selected_import) + imported_name = names[candidates.index(selected_import)] + return imports.get_changed_source(), imported_name diff --git a/venv/Lib/site-packages/rope/refactor/importutils/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/importutils/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4f94dc3ce42cf5918f806ddd52032315537be5d7 Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/importutils/__pycache__/__init__.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/importutils/__pycache__/actions.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/importutils/__pycache__/actions.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b5ab73c3668c70ef677a03426c8691d92faae459 Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/importutils/__pycache__/actions.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/importutils/__pycache__/importinfo.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/importutils/__pycache__/importinfo.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..380b2e75ecf9c5291c56b41b5ab982dd7f65d9cd Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/importutils/__pycache__/importinfo.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/importutils/__pycache__/module_imports.cpython-37.pyc b/venv/Lib/site-packages/rope/refactor/importutils/__pycache__/module_imports.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2ad17f6f643fb7842242a1a0e76270ffa61be423 Binary files /dev/null and b/venv/Lib/site-packages/rope/refactor/importutils/__pycache__/module_imports.cpython-37.pyc differ diff --git a/venv/Lib/site-packages/rope/refactor/importutils/actions.py b/venv/Lib/site-packages/rope/refactor/importutils/actions.py new file mode 100644 index 0000000000000000000000000000000000000000..fd0f70542b10521b2126d0134efa1db30d1e028b --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/importutils/actions.py @@ -0,0 +1,361 @@ +from rope.base import libutils +from rope.base import pyobjects, exceptions, stdmods +from rope.refactor import occurrences +from rope.refactor.importutils import importinfo + + +class ImportInfoVisitor(object): + + def dispatch(self, import_): + try: + method_name = 'visit' + import_.import_info.__class__.__name__ + method = getattr(self, method_name) + return method(import_, import_.import_info) + except exceptions.ModuleNotFoundError: + pass + + def visitEmptyImport(self, import_stmt, import_info): + pass + + def visitNormalImport(self, import_stmt, import_info): + pass + + def visitFromImport(self, import_stmt, import_info): + pass + + +class RelativeToAbsoluteVisitor(ImportInfoVisitor): + + def __init__(self, project, current_folder): + self.to_be_absolute = [] + self.project = project + self.folder = current_folder + self.context = importinfo.ImportContext(project, current_folder) + + def visitNormalImport(self, import_stmt, import_info): + self.to_be_absolute.extend( + self._get_relative_to_absolute_list(import_info)) + new_pairs = [] + for name, alias in import_info.names_and_aliases: + resource = self.project.find_module(name, folder=self.folder) + if resource is None: + new_pairs.append((name, alias)) + continue + absolute_name = libutils.modname(resource) + new_pairs.append((absolute_name, alias)) + if not import_info._are_name_and_alias_lists_equal( + new_pairs, import_info.names_and_aliases): + import_stmt.import_info = importinfo.NormalImport(new_pairs) + + def _get_relative_to_absolute_list(self, import_info): + result = [] + for name, alias in import_info.names_and_aliases: + if alias is not None: + continue + resource = self.project.find_module(name, folder=self.folder) + if resource is None: + continue + absolute_name = libutils.modname(resource) + if absolute_name != name: + result.append((name, absolute_name)) + return result + + def visitFromImport(self, import_stmt, import_info): + resource = import_info.get_imported_resource(self.context) + if resource is None: + return None + absolute_name = libutils.modname(resource) + if import_info.module_name != absolute_name: + import_stmt.import_info = importinfo.FromImport( + absolute_name, 0, import_info.names_and_aliases) + + +class FilteringVisitor(ImportInfoVisitor): + + def __init__(self, project, folder, can_select): + self.to_be_absolute = [] + self.project = project + self.can_select = self._transform_can_select(can_select) + self.context = importinfo.ImportContext(project, folder) + + def _transform_can_select(self, can_select): + def can_select_name_and_alias(name, alias): + imported = name + if alias is not None: + imported = alias + return can_select(imported) + return can_select_name_and_alias + + def visitNormalImport(self, import_stmt, import_info): + new_pairs = [] + for name, alias in import_info.names_and_aliases: + if self.can_select(name, alias): + new_pairs.append((name, alias)) + return importinfo.NormalImport(new_pairs) + + def visitFromImport(self, import_stmt, import_info): + if _is_future(import_info): + return import_info + new_pairs = [] + if import_info.is_star_import(): + for name in import_info.get_imported_names(self.context): + if self.can_select(name, None): + new_pairs.append(import_info.names_and_aliases[0]) + break + else: + for name, alias in import_info.names_and_aliases: + if self.can_select(name, alias): + new_pairs.append((name, alias)) + return importinfo.FromImport( + import_info.module_name, import_info.level, new_pairs) + + +class RemovingVisitor(ImportInfoVisitor): + + def __init__(self, project, folder, can_select): + self.to_be_absolute = [] + self.project = project + self.filtering = FilteringVisitor(project, folder, can_select) + + def dispatch(self, import_): + result = self.filtering.dispatch(import_) + if result is not None: + import_.import_info = result + + +class AddingVisitor(ImportInfoVisitor): + """A class for adding imports + + Given a list of `ImportInfo`\s, it tries to add each import to the + module and returns `True` and gives up when an import can be added + to older ones. + + """ + + def __init__(self, project, import_list): + self.project = project + self.import_list = import_list + self.import_info = None + + def dispatch(self, import_): + for import_info in self.import_list: + self.import_info = import_info + if ImportInfoVisitor.dispatch(self, import_): + return True + + # TODO: Handle adding relative and absolute imports + def visitNormalImport(self, import_stmt, import_info): + if not isinstance(self.import_info, import_info.__class__): + return False + # Adding ``import x`` and ``import x.y`` that results ``import x.y`` + if len(import_info.names_and_aliases) == \ + len(self.import_info.names_and_aliases) == 1: + imported1 = import_info.names_and_aliases[0] + imported2 = self.import_info.names_and_aliases[0] + if imported1[1] == imported2[1] is None: + if imported1[0].startswith(imported2[0] + '.'): + return True + if imported2[0].startswith(imported1[0] + '.'): + import_stmt.import_info = self.import_info + return True + # Multiple imports using a single import statement is discouraged + # so we won't bother adding them. + if self.import_info._are_name_and_alias_lists_equal( + import_info.names_and_aliases, + self.import_info.names_and_aliases): + return True + + def visitFromImport(self, import_stmt, import_info): + if isinstance(self.import_info, import_info.__class__) and \ + import_info.module_name == self.import_info.module_name and \ + import_info.level == self.import_info.level: + if import_info.is_star_import(): + return True + if self.import_info.is_star_import(): + import_stmt.import_info = self.import_info + return True + if self.project.prefs.get("split_imports"): + return self.import_info.names_and_aliases == \ + import_info.names_and_aliases + new_pairs = list(import_info.names_and_aliases) + for pair in self.import_info.names_and_aliases: + if pair not in new_pairs: + new_pairs.append(pair) + import_stmt.import_info = importinfo.FromImport( + import_info.module_name, import_info.level, new_pairs) + return True + + +class ExpandStarsVisitor(ImportInfoVisitor): + + def __init__(self, project, folder, can_select): + self.project = project + self.filtering = FilteringVisitor(project, folder, can_select) + self.context = importinfo.ImportContext(project, folder) + + def visitNormalImport(self, import_stmt, import_info): + self.filtering.dispatch(import_stmt) + + def visitFromImport(self, import_stmt, import_info): + if import_info.is_star_import(): + new_pairs = [] + for name in import_info.get_imported_names(self.context): + new_pairs.append((name, None)) + new_import = importinfo.FromImport( + import_info.module_name, import_info.level, new_pairs) + import_stmt.import_info = \ + self.filtering.visitFromImport(None, new_import) + else: + self.filtering.dispatch(import_stmt) + + +class SelfImportVisitor(ImportInfoVisitor): + + def __init__(self, project, current_folder, resource): + self.project = project + self.folder = current_folder + self.resource = resource + self.to_be_fixed = set() + self.to_be_renamed = set() + self.context = importinfo.ImportContext(project, current_folder) + + def visitNormalImport(self, import_stmt, import_info): + new_pairs = [] + for name, alias in import_info.names_and_aliases: + resource = self.project.find_module(name, folder=self.folder) + if resource is not None and resource == self.resource: + imported = name + if alias is not None: + imported = alias + self.to_be_fixed.add(imported) + else: + new_pairs.append((name, alias)) + if not import_info._are_name_and_alias_lists_equal( + new_pairs, import_info.names_and_aliases): + import_stmt.import_info = importinfo.NormalImport(new_pairs) + + def visitFromImport(self, import_stmt, import_info): + resource = import_info.get_imported_resource(self.context) + if resource is None: + return + if resource == self.resource: + self._importing_names_from_self(import_info, import_stmt) + return + pymodule = self.project.get_pymodule(resource) + new_pairs = [] + for name, alias in import_info.names_and_aliases: + try: + result = pymodule[name].get_object() + if isinstance(result, pyobjects.PyModule) and \ + result.get_resource() == self.resource: + imported = name + if alias is not None: + imported = alias + self.to_be_fixed.add(imported) + else: + new_pairs.append((name, alias)) + except exceptions.AttributeNotFoundError: + new_pairs.append((name, alias)) + if not import_info._are_name_and_alias_lists_equal( + new_pairs, import_info.names_and_aliases): + import_stmt.import_info = importinfo.FromImport( + import_info.module_name, import_info.level, new_pairs) + + def _importing_names_from_self(self, import_info, import_stmt): + if not import_info.is_star_import(): + for name, alias in import_info.names_and_aliases: + if alias is not None: + self.to_be_renamed.add((alias, name)) + import_stmt.empty_import() + + +class SortingVisitor(ImportInfoVisitor): + + def __init__(self, project, current_folder): + self.project = project + self.folder = current_folder + self.standard = set() + self.third_party = set() + self.in_project = set() + self.future = set() + self.context = importinfo.ImportContext(project, current_folder) + + def visitNormalImport(self, import_stmt, import_info): + if import_info.names_and_aliases: + name, alias = import_info.names_and_aliases[0] + resource = self.project.find_module( + name, folder=self.folder) + self._check_imported_resource(import_stmt, resource, name) + + def visitFromImport(self, import_stmt, import_info): + resource = import_info.get_imported_resource(self.context) + self._check_imported_resource(import_stmt, resource, + import_info.module_name) + + def _check_imported_resource(self, import_stmt, resource, imported_name): + info = import_stmt.import_info + if resource is not None and resource.project == self.project: + self.in_project.add(import_stmt) + elif _is_future(info): + self.future.add(import_stmt) + elif imported_name.split('.')[0] in stdmods.standard_modules(): + self.standard.add(import_stmt) + else: + self.third_party.add(import_stmt) + + +class LongImportVisitor(ImportInfoVisitor): + + def __init__(self, current_folder, project, maxdots, maxlength): + self.maxdots = maxdots + self.maxlength = maxlength + self.to_be_renamed = set() + self.current_folder = current_folder + self.project = project + self.new_imports = [] + + def visitNormalImport(self, import_stmt, import_info): + for name, alias in import_info.names_and_aliases: + if alias is None and self._is_long(name): + self.to_be_renamed.add(name) + last_dot = name.rindex('.') + from_ = name[:last_dot] + imported = name[last_dot + 1:] + self.new_imports.append( + importinfo.FromImport(from_, 0, ((imported, None), ))) + + def _is_long(self, name): + return name.count('.') > self.maxdots or \ + ('.' in name and len(name) > self.maxlength) + + +class RemovePyNameVisitor(ImportInfoVisitor): + + def __init__(self, project, pymodule, pyname, folder): + self.pymodule = pymodule + self.pyname = pyname + self.context = importinfo.ImportContext(project, folder) + + def visitFromImport(self, import_stmt, import_info): + new_pairs = [] + if not import_info.is_star_import(): + for name, alias in import_info.names_and_aliases: + try: + pyname = self.pymodule[alias or name] + if occurrences.same_pyname(self.pyname, pyname): + continue + except exceptions.AttributeNotFoundError: + pass + new_pairs.append((name, alias)) + return importinfo.FromImport( + import_info.module_name, import_info.level, new_pairs) + + def dispatch(self, import_): + result = ImportInfoVisitor.dispatch(self, import_) + if result is not None: + import_.import_info = result + + +def _is_future(info): + return isinstance(info, importinfo.FromImport) and \ + info.module_name == '__future__' diff --git a/venv/Lib/site-packages/rope/refactor/importutils/importinfo.py b/venv/Lib/site-packages/rope/refactor/importutils/importinfo.py new file mode 100644 index 0000000000000000000000000000000000000000..114080aac486abddf7e14025bc97733e7177ab70 --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/importutils/importinfo.py @@ -0,0 +1,201 @@ +class ImportStatement(object): + """Represent an import in a module + + `readonly` attribute controls whether this import can be changed + by import actions or not. + + """ + + def __init__(self, import_info, start_line, end_line, + main_statement=None, blank_lines=0): + self.start_line = start_line + self.end_line = end_line + self.readonly = False + self.main_statement = main_statement + self._import_info = None + self.import_info = import_info + self._is_changed = False + self.new_start = None + self.blank_lines = blank_lines + + def _get_import_info(self): + return self._import_info + + def _set_import_info(self, new_import): + if not self.readonly and \ + new_import is not None and not new_import == self._import_info: + self._is_changed = True + self._import_info = new_import + + import_info = property(_get_import_info, _set_import_info) + + def get_import_statement(self): + if self._is_changed or self.main_statement is None: + return self.import_info.get_import_statement() + else: + return self.main_statement + + def empty_import(self): + self.import_info = ImportInfo.get_empty_import() + + def move(self, lineno, blank_lines=0): + self.new_start = lineno + self.blank_lines = blank_lines + + def get_old_location(self): + return self.start_line, self.end_line + + def get_new_start(self): + return self.new_start + + def is_changed(self): + return self._is_changed or (self.new_start is not None or + self.new_start != self.start_line) + + def accept(self, visitor): + return visitor.dispatch(self) + + +class ImportInfo(object): + + def get_imported_primaries(self, context): + pass + + def get_imported_names(self, context): + return [primary.split('.')[0] + for primary in self.get_imported_primaries(context)] + + def get_import_statement(self): + pass + + def is_empty(self): + pass + + def __hash__(self): + return hash(self.get_import_statement()) + + def _are_name_and_alias_lists_equal(self, list1, list2): + if len(list1) != len(list2): + return False + for pair1, pair2 in zip(list1, list2): + if pair1 != pair2: + return False + return True + + def __eq__(self, obj): + return isinstance(obj, self.__class__) and \ + self.get_import_statement() == obj.get_import_statement() + + def __ne__(self, obj): + return not self.__eq__(obj) + + @staticmethod + def get_empty_import(): + return EmptyImport() + + +class NormalImport(ImportInfo): + + def __init__(self, names_and_aliases): + self.names_and_aliases = names_and_aliases + + def get_imported_primaries(self, context): + result = [] + for name, alias in self.names_and_aliases: + if alias: + result.append(alias) + else: + result.append(name) + return result + + def get_import_statement(self): + result = 'import ' + for name, alias in self.names_and_aliases: + result += name + if alias: + result += ' as ' + alias + result += ', ' + return result[:-2] + + def is_empty(self): + return len(self.names_and_aliases) == 0 + + +class FromImport(ImportInfo): + + def __init__(self, module_name, level, names_and_aliases): + self.module_name = module_name + self.level = level + self.names_and_aliases = names_and_aliases + + def get_imported_primaries(self, context): + if self.names_and_aliases[0][0] == '*': + module = self.get_imported_module(context) + return [name for name in module + if not name.startswith('_')] + result = [] + for name, alias in self.names_and_aliases: + if alias: + result.append(alias) + else: + result.append(name) + return result + + def get_imported_resource(self, context): + """Get the imported resource + + Returns `None` if module was not found. + """ + if self.level == 0: + return context.project.find_module( + self.module_name, folder=context.folder) + else: + return context.project.find_relative_module( + self.module_name, context.folder, self.level) + + def get_imported_module(self, context): + """Get the imported `PyModule` + + Raises `rope.base.exceptions.ModuleNotFoundError` if module + could not be found. + """ + if self.level == 0: + return context.project.get_module( + self.module_name, context.folder) + else: + return context.project.get_relative_module( + self.module_name, context.folder, self.level) + + def get_import_statement(self): + result = 'from ' + '.' * self.level + self.module_name + ' import ' + for name, alias in self.names_and_aliases: + result += name + if alias: + result += ' as ' + alias + result += ', ' + return result[:-2] + + def is_empty(self): + return len(self.names_and_aliases) == 0 + + def is_star_import(self): + return len(self.names_and_aliases) > 0 and \ + self.names_and_aliases[0][0] == '*' + + +class EmptyImport(ImportInfo): + + names_and_aliases = [] + + def is_empty(self): + return True + + def get_imported_primaries(self, context): + return [] + + +class ImportContext(object): + + def __init__(self, project, folder): + self.project = project + self.folder = folder diff --git a/venv/Lib/site-packages/rope/refactor/importutils/module_imports.py b/venv/Lib/site-packages/rope/refactor/importutils/module_imports.py new file mode 100644 index 0000000000000000000000000000000000000000..07cc4c71454cf27b34c14c66ef6f48519a34ce0f --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/importutils/module_imports.py @@ -0,0 +1,506 @@ +from rope.base import ast +from rope.base import pynames +from rope.base import utils +from rope.refactor.importutils import actions +from rope.refactor.importutils import importinfo + + +class ModuleImports(object): + + def __init__(self, project, pymodule, import_filter=None): + self.project = project + self.pymodule = pymodule + self.separating_lines = 0 + self.filter = import_filter + self.sorted = False + + @property + @utils.saveit + def imports(self): + finder = _GlobalImportFinder(self.pymodule) + result = finder.find_import_statements() + self.separating_lines = finder.get_separating_line_count() + if self.filter is not None: + for import_stmt in result: + if not self.filter(import_stmt): + import_stmt.readonly = True + return result + + def _get_unbound_names(self, defined_pyobject): + visitor = _GlobalUnboundNameFinder(self.pymodule, defined_pyobject) + ast.walk(self.pymodule.get_ast(), visitor) + return visitor.unbound + + def remove_unused_imports(self): + can_select = _OneTimeSelector(self._get_unbound_names(self.pymodule)) + visitor = actions.RemovingVisitor( + self.project, self._current_folder(), can_select) + for import_statement in self.imports: + import_statement.accept(visitor) + + def get_used_imports(self, defined_pyobject): + result = [] + can_select = _OneTimeSelector( + self._get_unbound_names(defined_pyobject)) + visitor = actions.FilteringVisitor( + self.project, self._current_folder(), can_select) + for import_statement in self.imports: + new_import = import_statement.accept(visitor) + if new_import is not None and not new_import.is_empty(): + result.append(new_import) + return result + + def get_changed_source(self): + if (not self.project.prefs.get("pull_imports_to_top") and + not self.sorted): + return ''.join(self._rewrite_imports(self.imports)) + + # Make sure we forward a removed import's preceding blank + # lines count to the following import statement. + prev_stmt = None + for stmt in self.imports: + if prev_stmt is not None and prev_stmt.import_info.is_empty(): + stmt.blank_lines = max(prev_stmt.blank_lines, stmt.blank_lines) + prev_stmt = stmt + # The new list of imports. + imports = [stmt for stmt in self.imports + if not stmt.import_info.is_empty()] + + after_removing = self._remove_imports(self.imports) + first_non_blank = self._first_non_blank_line(after_removing, 0) + first_import = self._first_import_line() - 1 + result = [] + # Writing module docs + result.extend(after_removing[first_non_blank:first_import]) + # Writing imports + sorted_imports = sorted(imports, key=self._get_location) + for stmt in sorted_imports: + if stmt != sorted_imports[0]: + result.append('\n' * stmt.blank_lines) + result.append(stmt.get_import_statement() + '\n') + if sorted_imports and first_non_blank < len(after_removing): + result.append('\n' * self.separating_lines) + + # Writing the body + first_after_imports = self._first_non_blank_line(after_removing, + first_import) + result.extend(after_removing[first_after_imports:]) + return ''.join(result) + + def _get_import_location(self, stmt): + start = stmt.get_new_start() + if start is None: + start = stmt.get_old_location()[0] + return start + + def _get_location(self, stmt): + if stmt.get_new_start() is not None: + return stmt.get_new_start() + else: + return stmt.get_old_location()[0] + + def _remove_imports(self, imports): + lines = self.pymodule.source_code.splitlines(True) + after_removing = [] + first_import_line = self._first_import_line() + last_index = 0 + for stmt in imports: + start, end = stmt.get_old_location() + blank_lines = 0 + if start != first_import_line: + blank_lines = _count_blank_lines(lines.__getitem__, start - 2, + last_index - 1, -1) + after_removing.extend(lines[last_index:start - 1 - blank_lines]) + last_index = end - 1 + after_removing.extend(lines[last_index:]) + return after_removing + + def _rewrite_imports(self, imports): + lines = self.pymodule.source_code.splitlines(True) + after_rewriting = [] + last_index = 0 + for stmt in imports: + start, end = stmt.get_old_location() + after_rewriting.extend(lines[last_index:start - 1]) + if not stmt.import_info.is_empty(): + after_rewriting.append(stmt.get_import_statement() + '\n') + last_index = end - 1 + after_rewriting.extend(lines[last_index:]) + return after_rewriting + + def _first_non_blank_line(self, lines, lineno): + return lineno + _count_blank_lines(lines.__getitem__, lineno, + len(lines)) + + def add_import(self, import_info): + visitor = actions.AddingVisitor(self.project, [import_info]) + for import_statement in self.imports: + if import_statement.accept(visitor): + break + else: + lineno = self._get_new_import_lineno() + blanks = self._get_new_import_blanks() + self.imports.append(importinfo.ImportStatement( + import_info, lineno, lineno, + blank_lines=blanks)) + + def _get_new_import_blanks(self): + return 0 + + def _get_new_import_lineno(self): + if self.imports: + return self.imports[-1].end_line + return 1 + + def filter_names(self, can_select): + visitor = actions.RemovingVisitor( + self.project, self._current_folder(), can_select) + for import_statement in self.imports: + import_statement.accept(visitor) + + def expand_stars(self): + can_select = _OneTimeSelector(self._get_unbound_names(self.pymodule)) + visitor = actions.ExpandStarsVisitor( + self.project, self._current_folder(), can_select) + for import_statement in self.imports: + import_statement.accept(visitor) + + def remove_duplicates(self): + added_imports = [] + for import_stmt in self.imports: + visitor = actions.AddingVisitor(self.project, + [import_stmt.import_info]) + for added_import in added_imports: + if added_import.accept(visitor): + import_stmt.empty_import() + else: + added_imports.append(import_stmt) + + def force_single_imports(self): + """force a single import per statement""" + for import_stmt in self.imports[:]: + import_info = import_stmt.import_info + if import_info.is_empty() or import_stmt.readonly: + continue + if len(import_info.names_and_aliases) > 1: + for name_and_alias in import_info.names_and_aliases: + if hasattr(import_info, "module_name"): + new_import = importinfo.FromImport( + import_info.module_name, import_info.level, + [name_and_alias]) + else: + new_import = importinfo.NormalImport([name_and_alias]) + self.add_import(new_import) + import_stmt.empty_import() + + def get_relative_to_absolute_list(self): + visitor = actions.RelativeToAbsoluteVisitor( + self.project, self._current_folder()) + for import_stmt in self.imports: + if not import_stmt.readonly: + import_stmt.accept(visitor) + return visitor.to_be_absolute + + def get_self_import_fix_and_rename_list(self): + visitor = actions.SelfImportVisitor( + self.project, self._current_folder(), self.pymodule.get_resource()) + for import_stmt in self.imports: + if not import_stmt.readonly: + import_stmt.accept(visitor) + return visitor.to_be_fixed, visitor.to_be_renamed + + def _current_folder(self): + return self.pymodule.get_resource().parent + + def sort_imports(self): + if self.project.prefs.get("sort_imports_alphabetically"): + sort_kwargs = dict(key=self._get_import_name) + else: + sort_kwargs = dict(key=self._key_imports) + + # IDEA: Sort from import list + visitor = actions.SortingVisitor(self.project, self._current_folder()) + for import_statement in self.imports: + import_statement.accept(visitor) + in_projects = sorted(visitor.in_project, **sort_kwargs) + third_party = sorted(visitor.third_party, **sort_kwargs) + standards = sorted(visitor.standard, **sort_kwargs) + future = sorted(visitor.future, **sort_kwargs) + last_index = self._first_import_line() + last_index = self._move_imports(future, last_index, 0) + last_index = self._move_imports(standards, last_index, 1) + last_index = self._move_imports(third_party, last_index, 1) + last_index = self._move_imports(in_projects, last_index, 1) + self.separating_lines = 2 + self.sorted = True + + def _first_import_line(self): + nodes = self.pymodule.get_ast().body + lineno = 0 + if self.pymodule.get_doc() is not None: + lineno = 1 + if len(nodes) > lineno: + if (isinstance(nodes[lineno], ast.Import) or + isinstance(nodes[lineno], ast.ImportFrom)): + return nodes[lineno].lineno + lineno = self.pymodule.logical_lines.logical_line_in( + nodes[lineno].lineno)[0] + else: + lineno = self.pymodule.lines.length() + + return lineno - _count_blank_lines(self.pymodule.lines.get_line, + lineno - 1, 1, -1) + + def _get_import_name(self, import_stmt): + import_info = import_stmt.import_info + if hasattr(import_info, "module_name"): + return "%s.%s" % (import_info.module_name, + import_info.names_and_aliases[0][0]) + else: + return import_info.names_and_aliases[0][0] + + def _key_imports(self, stm1): + str1 = stm1.get_import_statement() + return str1.startswith("from "), str1 + + #str1 = stmt1.get_import_statement() + #str2 = stmt2.get_import_statement() + #if str1.startswith('from ') and not str2.startswith('from '): + # return 1 + #if not str1.startswith('from ') and str2.startswith('from '): + # return -1 + #return cmp(str1, str2) + + def _move_imports(self, imports, index, blank_lines): + if imports: + imports[0].move(index, blank_lines) + index += 1 + if len(imports) > 1: + for stmt in imports[1:]: + stmt.move(index) + index += 1 + return index + + def handle_long_imports(self, maxdots, maxlength): + visitor = actions.LongImportVisitor( + self._current_folder(), self.project, maxdots, maxlength) + for import_statement in self.imports: + if not import_statement.readonly: + import_statement.accept(visitor) + for import_info in visitor.new_imports: + self.add_import(import_info) + return visitor.to_be_renamed + + def remove_pyname(self, pyname): + """Removes pyname when imported in ``from mod import x``""" + visitor = actions.RemovePyNameVisitor(self.project, self.pymodule, + pyname, self._current_folder()) + for import_stmt in self.imports: + import_stmt.accept(visitor) + + +def _count_blank_lines(get_line, start, end, step=1): + count = 0 + for idx in range(start, end, step): + if get_line(idx).strip() == '': + count += 1 + else: + break + return count + + +class _OneTimeSelector(object): + + def __init__(self, names): + self.names = names + self.selected_names = set() + + def __call__(self, imported_primary): + if self._can_name_be_added(imported_primary): + for name in self._get_dotted_tokens(imported_primary): + self.selected_names.add(name) + return True + return False + + def _get_dotted_tokens(self, imported_primary): + tokens = imported_primary.split('.') + for i in range(len(tokens)): + yield '.'.join(tokens[:i + 1]) + + def _can_name_be_added(self, imported_primary): + for name in self._get_dotted_tokens(imported_primary): + if name in self.names and name not in self.selected_names: + return True + return False + + +class _UnboundNameFinder(object): + + def __init__(self, pyobject): + self.pyobject = pyobject + + def _visit_child_scope(self, node): + pyobject = self.pyobject.get_module().get_scope().\ + get_inner_scope_for_line(node.lineno).pyobject + visitor = _LocalUnboundNameFinder(pyobject, self) + for child in ast.get_child_nodes(node): + ast.walk(child, visitor) + + def _FunctionDef(self, node): + self._visit_child_scope(node) + + def _ClassDef(self, node): + self._visit_child_scope(node) + + def _Name(self, node): + if self._get_root()._is_node_interesting(node) and \ + not self.is_bound(node.id): + self.add_unbound(node.id) + + def _Attribute(self, node): + result = [] + while isinstance(node, ast.Attribute): + result.append(node.attr) + node = node.value + if isinstance(node, ast.Name): + result.append(node.id) + primary = '.'.join(reversed(result)) + if self._get_root()._is_node_interesting(node) and \ + not self.is_bound(primary): + self.add_unbound(primary) + else: + ast.walk(node, self) + + def _get_root(self): + pass + + def is_bound(self, name, propagated=False): + pass + + def add_unbound(self, name): + pass + + +class _GlobalUnboundNameFinder(_UnboundNameFinder): + + def __init__(self, pymodule, wanted_pyobject): + super(_GlobalUnboundNameFinder, self).__init__(pymodule) + self.unbound = set() + self.names = set() + for name, pyname in pymodule._get_structural_attributes().items(): + if not isinstance(pyname, (pynames.ImportedName, + pynames.ImportedModule)): + self.names.add(name) + wanted_scope = wanted_pyobject.get_scope() + self.start = wanted_scope.get_start() + self.end = wanted_scope.get_end() + 1 + + def _get_root(self): + return self + + def is_bound(self, primary, propagated=False): + name = primary.split('.')[0] + if name in self.names: + return True + return False + + def add_unbound(self, name): + names = name.split('.') + for i in range(len(names)): + self.unbound.add('.'.join(names[:i + 1])) + + def _is_node_interesting(self, node): + return self.start <= node.lineno < self.end + + +class _LocalUnboundNameFinder(_UnboundNameFinder): + + def __init__(self, pyobject, parent): + super(_LocalUnboundNameFinder, self).__init__(pyobject) + self.parent = parent + + def _get_root(self): + return self.parent._get_root() + + def is_bound(self, primary, propagated=False): + name = primary.split('.')[0] + if propagated: + names = self.pyobject.get_scope().get_propagated_names() + else: + names = self.pyobject.get_scope().get_names() + if name in names or self.parent.is_bound(name, propagated=True): + return True + return False + + def add_unbound(self, name): + self.parent.add_unbound(name) + + +class _GlobalImportFinder(object): + + def __init__(self, pymodule): + self.current_folder = None + if pymodule.get_resource(): + self.current_folder = pymodule.get_resource().parent + self.pymodule = pymodule + self.imports = [] + self.pymodule = pymodule + self.lines = self.pymodule.lines + + def visit_import(self, node, end_line): + start_line = node.lineno + import_statement = importinfo.ImportStatement( + importinfo.NormalImport(self._get_names(node.names)), + start_line, end_line, self._get_text(start_line, end_line), + blank_lines=self._count_empty_lines_before(start_line)) + self.imports.append(import_statement) + + def _count_empty_lines_before(self, lineno): + return _count_blank_lines(self.lines.get_line, lineno - 1, 0, -1) + + def _count_empty_lines_after(self, lineno): + return _count_blank_lines(self.lines.get_line, lineno + 1, + self.lines.length()) + + def get_separating_line_count(self): + if not self.imports: + return 0 + return self._count_empty_lines_after(self.imports[-1].end_line - 1) + + def _get_text(self, start_line, end_line): + result = [] + for index in range(start_line, end_line): + result.append(self.lines.get_line(index)) + return '\n'.join(result) + + def visit_from(self, node, end_line): + level = 0 + if node.level: + level = node.level + import_info = importinfo.FromImport( + node.module or '', # see comment at rope.base.ast.walk + level, self._get_names(node.names)) + start_line = node.lineno + self.imports.append(importinfo.ImportStatement( + import_info, node.lineno, end_line, + self._get_text(start_line, end_line), + blank_lines= + self._count_empty_lines_before(start_line))) + + def _get_names(self, alias_names): + result = [] + for alias in alias_names: + result.append((alias.name, alias.asname)) + return result + + def find_import_statements(self): + nodes = self.pymodule.get_ast().body + for index, node in enumerate(nodes): + if isinstance(node, (ast.Import, ast.ImportFrom)): + lines = self.pymodule.logical_lines + end_line = lines.logical_line_in(node.lineno)[1] + 1 + if isinstance(node, ast.Import): + self.visit_import(node, end_line) + if isinstance(node, ast.ImportFrom): + self.visit_from(node, end_line) + return self.imports diff --git a/venv/Lib/site-packages/rope/refactor/inline.py b/venv/Lib/site-packages/rope/refactor/inline.py new file mode 100644 index 0000000000000000000000000000000000000000..467edefaa16facf95ef77406a32ce54e95688fad --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/inline.py @@ -0,0 +1,625 @@ +# Known Bugs when inlining a function/method +# The values passed to function are inlined using _inlined_variable. +# This may cause two problems, illustrated in the examples below +# +# def foo(var1): +# var1 = var1*10 +# return var1 +# +# If a call to foo(20) is inlined, the result of inlined function is 20, +# but it should be 200. +# +# def foo(var1): +# var2 = var1*10 +# return var2 +# +# 2- If a call to foo(10+10) is inlined the result of inlined function is 110 +# but it should be 200. + +import re + +import rope.base.exceptions +import rope.refactor.functionutils +from rope.base import (pynames, pyobjects, codeanalyze, + taskhandle, evaluate, worder, utils, libutils) +from rope.base.change import ChangeSet, ChangeContents +from rope.refactor import (occurrences, rename, sourceutils, + importutils, move, change_signature) + + +def unique_prefix(): + n = 0 + while True: + yield "__" + str(n) + "__" + n += 1 + + +def create_inline(project, resource, offset): + """Create a refactoring object for inlining + + Based on `resource` and `offset` it returns an instance of + `InlineMethod`, `InlineVariable` or `InlineParameter`. + + """ + pyname = _get_pyname(project, resource, offset) + message = 'Inline refactoring should be performed on ' \ + 'a method, local variable or parameter.' + if pyname is None: + raise rope.base.exceptions.RefactoringError(message) + if isinstance(pyname, pynames.ImportedName): + pyname = pyname._get_imported_pyname() + if isinstance(pyname, pynames.AssignedName): + return InlineVariable(project, resource, offset) + if isinstance(pyname, pynames.ParameterName): + return InlineParameter(project, resource, offset) + if isinstance(pyname.get_object(), pyobjects.PyFunction): + return InlineMethod(project, resource, offset) + else: + raise rope.base.exceptions.RefactoringError(message) + + +class _Inliner(object): + + def __init__(self, project, resource, offset): + self.project = project + self.pyname = _get_pyname(project, resource, offset) + range_finder = worder.Worder(resource.read(), True) + self.region = range_finder.get_primary_range(offset) + self.name = range_finder.get_word_at(offset) + self.offset = offset + self.original = resource + + def get_changes(self, *args, **kwds): + pass + + def get_kind(self): + """Return either 'variable', 'method' or 'parameter'""" + + +class InlineMethod(_Inliner): + + def __init__(self, *args, **kwds): + super(InlineMethod, self).__init__(*args, **kwds) + self.pyfunction = self.pyname.get_object() + self.pymodule = self.pyfunction.get_module() + self.resource = self.pyfunction.get_module().get_resource() + self.occurrence_finder = occurrences.create_finder( + self.project, self.name, self.pyname) + self.normal_generator = _DefinitionGenerator(self.project, + self.pyfunction) + self._init_imports() + + def _init_imports(self): + body = sourceutils.get_body(self.pyfunction) + body, imports = move.moving_code_with_imports( + self.project, self.resource, body) + self.imports = imports + self.others_generator = _DefinitionGenerator( + self.project, self.pyfunction, body=body) + + def _get_scope_range(self): + scope = self.pyfunction.get_scope() + lines = self.pymodule.lines + start_line = scope.get_start() + if self.pyfunction.decorators: + decorators = self.pyfunction.decorators + if hasattr(decorators[0], 'lineno'): + start_line = decorators[0].lineno + start_offset = lines.get_line_start(start_line) + end_offset = min(lines.get_line_end(scope.end) + 1, + len(self.pymodule.source_code)) + return (start_offset, end_offset) + + def get_changes(self, remove=True, only_current=False, resources=None, + task_handle=taskhandle.NullTaskHandle()): + """Get the changes this refactoring makes + + If `remove` is `False` the definition will not be removed. If + `only_current` is `True`, the the current occurrence will be + inlined, only. + """ + changes = ChangeSet('Inline method <%s>' % self.name) + if resources is None: + resources = self.project.get_python_files() + if only_current: + resources = [self.original] + if remove: + resources.append(self.resource) + job_set = task_handle.create_jobset('Collecting Changes', + len(resources)) + for file in resources: + job_set.started_job(file.path) + if file == self.resource: + changes.add_change(self._defining_file_changes( + changes, remove=remove, only_current=only_current)) + else: + aim = None + if only_current and self.original == file: + aim = self.offset + handle = _InlineFunctionCallsForModuleHandle( + self.project, file, self.others_generator, aim) + result = move.ModuleSkipRenamer( + self.occurrence_finder, file, handle).get_changed_module() + if result is not None: + result = _add_imports(self.project, result, + file, self.imports) + if remove: + result = _remove_from(self.project, self.pyname, + result, file) + changes.add_change(ChangeContents(file, result)) + job_set.finished_job() + return changes + + def _get_removed_range(self): + scope = self.pyfunction.get_scope() + lines = self.pymodule.lines + start, end = self._get_scope_range() + end_line = scope.get_end() + for i in range(end_line + 1, lines.length()): + if lines.get_line(i).strip() == '': + end_line = i + else: + break + end = min(lines.get_line_end(end_line) + 1, + len(self.pymodule.source_code)) + return (start, end) + + def _defining_file_changes(self, changes, remove, only_current): + start_offset, end_offset = self._get_removed_range() + aim = None + if only_current: + if self.resource == self.original: + aim = self.offset + else: + # we don't want to change any of them + aim = len(self.resource.read()) + 100 + handle = _InlineFunctionCallsForModuleHandle( + self.project, self.resource, + self.normal_generator, aim_offset=aim) + replacement = None + if remove: + replacement = self._get_method_replacement() + result = move.ModuleSkipRenamer( + self.occurrence_finder, self.resource, handle, start_offset, + end_offset, replacement).get_changed_module() + return ChangeContents(self.resource, result) + + def _get_method_replacement(self): + if self._is_the_last_method_of_a_class(): + indents = sourceutils.get_indents( + self.pymodule.lines, self.pyfunction.get_scope().get_start()) + return ' ' * indents + 'pass\n' + return '' + + def _is_the_last_method_of_a_class(self): + pyclass = self.pyfunction.parent + if not isinstance(pyclass, pyobjects.PyClass): + return False + class_start, class_end = sourceutils.get_body_region(pyclass) + source = self.pymodule.source_code + func_start, func_end = self._get_scope_range() + if source[class_start:func_start].strip() == '' and \ + source[func_end:class_end].strip() == '': + return True + return False + + def get_kind(self): + return 'method' + + +class InlineVariable(_Inliner): + + def __init__(self, *args, **kwds): + super(InlineVariable, self).__init__(*args, **kwds) + self.pymodule = self.pyname.get_definition_location()[0] + self.resource = self.pymodule.get_resource() + self._check_exceptional_conditions() + self._init_imports() + + def _check_exceptional_conditions(self): + if len(self.pyname.assignments) != 1: + raise rope.base.exceptions.RefactoringError( + 'Local variable should be assigned once for inlining.') + + def get_changes(self, remove=True, only_current=False, resources=None, + docs=False, task_handle=taskhandle.NullTaskHandle()): + if resources is None: + if rename._is_local(self.pyname): + resources = [self.resource] + else: + resources = self.project.get_python_files() + if only_current: + resources = [self.original] + if remove and self.original != self.resource: + resources.append(self.resource) + changes = ChangeSet('Inline variable <%s>' % self.name) + jobset = task_handle.create_jobset('Calculating changes', + len(resources)) + + for resource in resources: + jobset.started_job(resource.path) + if resource == self.resource: + source = self._change_main_module(remove, only_current, docs) + changes.add_change(ChangeContents(self.resource, source)) + else: + result = self._change_module(resource, remove, only_current) + if result is not None: + result = _add_imports(self.project, result, + resource, self.imports) + changes.add_change(ChangeContents(resource, result)) + jobset.finished_job() + return changes + + def _change_main_module(self, remove, only_current, docs): + region = None + if only_current and self.original == self.resource: + region = self.region + return _inline_variable(self.project, self.pymodule, self.pyname, + self.name, remove=remove, region=region, + docs=docs) + + def _init_imports(self): + vardef = _getvardef(self.pymodule, self.pyname) + self.imported, self.imports = move.moving_code_with_imports( + self.project, self.resource, vardef) + + def _change_module(self, resource, remove, only_current): + filters = [occurrences.NoImportsFilter(), + occurrences.PyNameFilter(self.pyname)] + if only_current and resource == self.original: + def check_aim(occurrence): + start, end = occurrence.get_primary_range() + if self.offset < start or end < self.offset: + return False + filters.insert(0, check_aim) + finder = occurrences.Finder(self.project, self.name, filters=filters) + changed = rename.rename_in_module( + finder, self.imported, resource=resource, replace_primary=True) + if changed and remove: + changed = _remove_from(self.project, self.pyname, + changed, resource) + return changed + + def get_kind(self): + return 'variable' + + +class InlineParameter(_Inliner): + + def __init__(self, *args, **kwds): + super(InlineParameter, self).__init__(*args, **kwds) + resource, offset = self._function_location() + index = self.pyname.index + self.changers = [change_signature.ArgumentDefaultInliner(index)] + self.signature = change_signature.ChangeSignature(self.project, + resource, offset) + + def _function_location(self): + pymodule, lineno = self.pyname.get_definition_location() + resource = pymodule.get_resource() + start = pymodule.lines.get_line_start(lineno) + word_finder = worder.Worder(pymodule.source_code) + offset = word_finder.find_function_offset(start) + return resource, offset + + def get_changes(self, **kwds): + """Get the changes needed by this refactoring + + See `rope.refactor.change_signature.ChangeSignature.get_changes()` + for arguments. + """ + return self.signature.get_changes(self.changers, **kwds) + + def get_kind(self): + return 'parameter' + + +def _join_lines(lines): + definition_lines = [] + for unchanged_line in lines: + line = unchanged_line.strip() + if line.endswith('\\'): + line = line[:-1].strip() + definition_lines.append(line) + joined = ' '.join(definition_lines) + return joined + + +class _DefinitionGenerator(object): + unique_prefix = unique_prefix() + + def __init__(self, project, pyfunction, body=None): + self.project = project + self.pyfunction = pyfunction + self.pymodule = pyfunction.get_module() + self.resource = self.pymodule.get_resource() + self.definition_info = self._get_definition_info() + self.definition_params = self._get_definition_params() + self._calculated_definitions = {} + if body is not None: + self.body = body + else: + self.body = sourceutils.get_body(self.pyfunction) + + def _get_definition_info(self): + return rope.refactor.functionutils.DefinitionInfo.read(self.pyfunction) + + def _get_definition_params(self): + definition_info = self.definition_info + paramdict = dict([pair for pair in definition_info.args_with_defaults]) + if definition_info.args_arg is not None or \ + definition_info.keywords_arg is not None: + raise rope.base.exceptions.RefactoringError( + 'Cannot inline functions with list and keyword arguements.') + if self.pyfunction.get_kind() == 'classmethod': + paramdict[definition_info.args_with_defaults[0][0]] = \ + self.pyfunction.parent.get_name() + return paramdict + + def get_function_name(self): + return self.pyfunction.get_name() + + def get_definition(self, primary, pyname, call, host_vars=[], + returns=False): + # caching already calculated definitions + return self._calculate_definition(primary, pyname, call, + host_vars, returns) + + def _calculate_header(self, primary, pyname, call): + # A header is created which initializes parameters + # to the values passed to the function. + call_info = rope.refactor.functionutils.CallInfo.read( + primary, pyname, self.definition_info, call) + paramdict = self.definition_params + mapping = rope.refactor.functionutils.ArgumentMapping( + self.definition_info, call_info) + for param_name, value in mapping.param_dict.items(): + paramdict[param_name] = value + header = '' + to_be_inlined = [] + for name, value in paramdict.items(): + if name != value and value is not None: + header += name + ' = ' + value.replace('\n', ' ') + '\n' + to_be_inlined.append(name) + return header, to_be_inlined + + def _calculate_definition(self, primary, pyname, call, host_vars, returns): + + header, to_be_inlined = self._calculate_header(primary, pyname, call) + + source = header + self.body + mod = libutils.get_string_module(self.project, source) + name_dict = mod.get_scope().get_names() + all_names = [x for x in name_dict if + not isinstance(name_dict[x], + rope.base.builtins.BuiltinName)] + + # If there is a name conflict, all variable names + # inside the inlined function are renamed + if len(set(all_names).intersection(set(host_vars))) > 0: + + prefix = next(_DefinitionGenerator.unique_prefix) + guest = libutils.get_string_module(self.project, source, + self.resource) + + to_be_inlined = [prefix + item for item in to_be_inlined] + for item in all_names: + pyname = guest[item] + occurrence_finder = occurrences.create_finder(self.project, + item, pyname) + source = rename.rename_in_module(occurrence_finder, + prefix + item, pymodule=guest) + guest = libutils.get_string_module( + self.project, source, self.resource) + + #parameters not reassigned inside the functions are now inlined. + for name in to_be_inlined: + pymodule = libutils.get_string_module( + self.project, source, self.resource) + pyname = pymodule[name] + source = _inline_variable(self.project, pymodule, pyname, name) + + return self._replace_returns_with(source, returns) + + def _replace_returns_with(self, source, returns): + result = [] + returned = None + last_changed = 0 + for match in _DefinitionGenerator._get_return_pattern().finditer( + source): + for key, value in match.groupdict().items(): + if value and key == 'return': + result.append(source[last_changed:match.start('return')]) + if returns: + self._check_nothing_after_return(source, + match.end('return')) + beg_idx = match.end('return') + returned = _join_lines( + source[beg_idx:len(source)].splitlines()) + last_changed = len(source) + else: + current = match.end('return') + while current < len(source) and \ + source[current] in ' \t': + current += 1 + last_changed = current + if current == len(source) or source[current] == '\n': + result.append('pass') + result.append(source[last_changed:]) + return ''.join(result), returned + + def _check_nothing_after_return(self, source, offset): + lines = codeanalyze.SourceLinesAdapter(source) + lineno = lines.get_line_number(offset) + logical_lines = codeanalyze.LogicalLineFinder(lines) + lineno = logical_lines.logical_line_in(lineno)[1] + if source[lines.get_line_end(lineno):len(source)].strip() != '': + raise rope.base.exceptions.RefactoringError( + 'Cannot inline functions with statements ' + + 'after return statement.') + + @classmethod + def _get_return_pattern(cls): + if not hasattr(cls, '_return_pattern'): + def named_pattern(name, list_): + return "(?P<%s>" % name + "|".join(list_) + ")" + comment_pattern = named_pattern('comment', [r'#[^\n]*']) + string_pattern = named_pattern('string', + [codeanalyze.get_string_pattern()]) + return_pattern = r'\b(?P<return>return)\b' + cls._return_pattern = re.compile(comment_pattern + "|" + + string_pattern + "|" + + return_pattern) + return cls._return_pattern + + +class _InlineFunctionCallsForModuleHandle(object): + + def __init__(self, project, resource, + definition_generator, aim_offset=None): + """Inlines occurrences + + If `aim` is not `None` only the occurrences that intersect + `aim` offset will be inlined. + + """ + self.project = project + self.generator = definition_generator + self.resource = resource + self.aim = aim_offset + + def occurred_inside_skip(self, change_collector, occurrence): + if not occurrence.is_defined(): + raise rope.base.exceptions.RefactoringError( + 'Cannot inline functions that reference themselves') + + def occurred_outside_skip(self, change_collector, occurrence): + start, end = occurrence.get_primary_range() + # we remove out of date imports later + if occurrence.is_in_import_statement(): + return + # the function is referenced outside an import statement + if not occurrence.is_called(): + raise rope.base.exceptions.RefactoringError( + 'Reference to inlining function other than function call' + ' in <file: %s, offset: %d>' % (self.resource.path, start)) + if self.aim is not None and (self.aim < start or self.aim > end): + return + end_parens = self._find_end_parens(self.source, end - 1) + lineno = self.lines.get_line_number(start) + start_line, end_line = self.pymodule.logical_lines.\ + logical_line_in(lineno) + line_start = self.lines.get_line_start(start_line) + line_end = self.lines.get_line_end(end_line) + + returns = self.source[line_start:start].strip() != '' or \ + self.source[end_parens:line_end].strip() != '' + indents = sourceutils.get_indents(self.lines, start_line) + primary, pyname = occurrence.get_primary_and_pyname() + + host = self.pymodule + scope = host.scope.get_inner_scope_for_line(lineno) + definition, returned = self.generator.get_definition( + primary, pyname, self.source[start:end_parens], scope.get_names(), + returns=returns) + + end = min(line_end + 1, len(self.source)) + change_collector.add_change( + line_start, end, sourceutils.fix_indentation(definition, indents)) + if returns: + name = returned + if name is None: + name = 'None' + change_collector.add_change( + line_end, end, self.source[line_start:start] + name + + self.source[end_parens:end]) + + def _find_end_parens(self, source, offset): + finder = worder.Worder(source) + return finder.get_word_parens_range(offset)[1] + + @property + @utils.saveit + def pymodule(self): + return self.project.get_pymodule(self.resource) + + @property + @utils.saveit + def source(self): + if self.resource is not None: + return self.resource.read() + else: + return self.pymodule.source_code + + @property + @utils.saveit + def lines(self): + return self.pymodule.lines + + +def _inline_variable(project, pymodule, pyname, name, + remove=True, region=None, docs=False): + definition = _getvardef(pymodule, pyname) + start, end = _assigned_lineno(pymodule, pyname) + + occurrence_finder = occurrences.create_finder(project, name, pyname, + docs=docs) + changed_source = rename.rename_in_module( + occurrence_finder, definition, pymodule=pymodule, + replace_primary=True, writes=False, region=region) + if changed_source is None: + changed_source = pymodule.source_code + if remove: + lines = codeanalyze.SourceLinesAdapter(changed_source) + source = changed_source[:lines.get_line_start(start)] + \ + changed_source[lines.get_line_end(end) + 1:] + else: + source = changed_source + return source + + +def _getvardef(pymodule, pyname): + assignment = pyname.assignments[0] + lines = pymodule.lines + start, end = _assigned_lineno(pymodule, pyname) + definition_with_assignment = _join_lines( + [lines.get_line(n) for n in range(start, end + 1)]) + if assignment.levels: + raise rope.base.exceptions.RefactoringError( + 'Cannot inline tuple assignments.') + definition = definition_with_assignment[definition_with_assignment. + index('=') + 1:].strip() + return definition + + +def _assigned_lineno(pymodule, pyname): + definition_line = pyname.assignments[0].ast_node.lineno + return pymodule.logical_lines.logical_line_in(definition_line) + + +def _add_imports(project, source, resource, imports): + if not imports: + return source + pymodule = libutils.get_string_module(project, source, resource) + module_import = importutils.get_module_imports(project, pymodule) + for import_info in imports: + module_import.add_import(import_info) + source = module_import.get_changed_source() + pymodule = libutils.get_string_module(project, source, resource) + import_tools = importutils.ImportTools(project) + return import_tools.organize_imports(pymodule, unused=False, sort=False) + + +def _get_pyname(project, resource, offset): + pymodule = project.get_pymodule(resource) + pyname = evaluate.eval_location(pymodule, offset) + if isinstance(pyname, pynames.ImportedName): + pyname = pyname._get_imported_pyname() + return pyname + + +def _remove_from(project, pyname, source, resource): + pymodule = libutils.get_string_module(project, source, resource) + module_import = importutils.get_module_imports(project, pymodule) + module_import.remove_pyname(pyname) + return module_import.get_changed_source() diff --git a/venv/Lib/site-packages/rope/refactor/introduce_factory.py b/venv/Lib/site-packages/rope/refactor/introduce_factory.py new file mode 100644 index 0000000000000000000000000000000000000000..7532e361a808543b65c0bb9768964901ff90e15b --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/introduce_factory.py @@ -0,0 +1,135 @@ +import rope.base.exceptions +import rope.base.pyobjects +from rope.base import libutils +from rope.base import taskhandle, evaluate +from rope.base.change import (ChangeSet, ChangeContents) +from rope.refactor import rename, occurrences, sourceutils, importutils + + +class IntroduceFactory(object): + + def __init__(self, project, resource, offset): + self.project = project + self.offset = offset + + this_pymodule = self.project.get_pymodule(resource) + self.old_pyname = evaluate.eval_location(this_pymodule, offset) + if self.old_pyname is None or \ + not isinstance(self.old_pyname.get_object(), + rope.base.pyobjects.PyClass): + raise rope.base.exceptions.RefactoringError( + 'Introduce factory should be performed on a class.') + self.old_name = self.old_pyname.get_object().get_name() + self.pymodule = self.old_pyname.get_object().get_module() + self.resource = self.pymodule.get_resource() + + def get_changes(self, factory_name, global_factory=False, resources=None, + task_handle=taskhandle.NullTaskHandle()): + """Get the changes this refactoring makes + + `factory_name` indicates the name of the factory function to + be added. If `global_factory` is `True` the factory will be + global otherwise a static method is added to the class. + + `resources` can be a list of `rope.base.resource.File`\s that + this refactoring should be applied on; if `None` all python + files in the project are searched. + + """ + if resources is None: + resources = self.project.get_python_files() + changes = ChangeSet('Introduce factory method <%s>' % factory_name) + job_set = task_handle.create_jobset('Collecting Changes', + len(resources)) + self._change_module(resources, changes, factory_name, + global_factory, job_set) + return changes + + def get_name(self): + """Return the name of the class""" + return self.old_name + + def _change_module(self, resources, changes, + factory_name, global_, job_set): + if global_: + replacement = '__rope_factory_%s_' % factory_name + else: + replacement = self._new_function_name(factory_name, global_) + + for file_ in resources: + job_set.started_job(file_.path) + if file_ == self.resource: + self._change_resource(changes, factory_name, global_) + job_set.finished_job() + continue + changed_code = self._rename_occurrences(file_, replacement, + global_) + if changed_code is not None: + if global_: + new_pymodule = libutils.get_string_module( + self.project, changed_code, self.resource) + modname = libutils.modname(self.resource) + changed_code, imported = importutils.add_import( + self.project, new_pymodule, modname, factory_name) + changed_code = changed_code.replace(replacement, imported) + changes.add_change(ChangeContents(file_, changed_code)) + job_set.finished_job() + + def _change_resource(self, changes, factory_name, global_): + class_scope = self.old_pyname.get_object().get_scope() + source_code = self._rename_occurrences( + self.resource, self._new_function_name(factory_name, + global_), global_) + if source_code is None: + source_code = self.pymodule.source_code + else: + self.pymodule = libutils.get_string_module( + self.project, source_code, resource=self.resource) + lines = self.pymodule.lines + start = self._get_insertion_offset(class_scope, lines) + result = source_code[:start] + result += self._get_factory_method(lines, class_scope, + factory_name, global_) + result += source_code[start:] + changes.add_change(ChangeContents(self.resource, result)) + + def _get_insertion_offset(self, class_scope, lines): + start_line = class_scope.get_end() + if class_scope.get_scopes(): + start_line = class_scope.get_scopes()[-1].get_end() + start = lines.get_line_end(start_line) + 1 + return start + + def _get_factory_method(self, lines, class_scope, + factory_name, global_): + unit_indents = ' ' * sourceutils.get_indent(self.project) + if global_: + if self._get_scope_indents(lines, class_scope) > 0: + raise rope.base.exceptions.RefactoringError( + 'Cannot make global factory method for nested classes.') + return ('\ndef %s(*args, **kwds):\n%sreturn %s(*args, **kwds)\n' % + (factory_name, unit_indents, self.old_name)) + unindented_factory = \ + ('@staticmethod\ndef %s(*args, **kwds):\n' % factory_name + + '%sreturn %s(*args, **kwds)\n' % (unit_indents, self.old_name)) + indents = self._get_scope_indents(lines, class_scope) + \ + sourceutils.get_indent(self.project) + return '\n' + sourceutils.indent_lines(unindented_factory, indents) + + def _get_scope_indents(self, lines, scope): + return sourceutils.get_indents(lines, scope.get_start()) + + def _new_function_name(self, factory_name, global_): + if global_: + return factory_name + else: + return self.old_name + '.' + factory_name + + def _rename_occurrences(self, file_, changed_name, global_factory): + finder = occurrences.create_finder(self.project, self.old_name, + self.old_pyname, only_calls=True) + result = rename.rename_in_module(finder, changed_name, resource=file_, + replace_primary=global_factory) + return result + +IntroduceFactoryRefactoring = IntroduceFactory diff --git a/venv/Lib/site-packages/rope/refactor/introduce_parameter.py b/venv/Lib/site-packages/rope/refactor/introduce_parameter.py new file mode 100644 index 0000000000000000000000000000000000000000..43d6f755b1170bf720e0a5d4558a1810278f29cd --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/introduce_parameter.py @@ -0,0 +1,96 @@ +import rope.base.change +from rope.base import exceptions, evaluate, worder, codeanalyze +from rope.refactor import functionutils, sourceutils, occurrences + + +class IntroduceParameter(object): + """Introduce parameter refactoring + + This refactoring adds a new parameter to a function and replaces + references to an expression in it with the new parameter. + + The parameter finding part is different from finding similar + pieces in extract refactorings. In this refactoring parameters + are found based on the object they reference to. For instance + in:: + + class A(object): + var = None + + class B(object): + a = A() + + b = B() + a = b.a + + def f(a): + x = b.a.var + a.var + + using this refactoring on ``a.var`` with ``p`` as the new + parameter name, will result in:: + + def f(p=a.var): + x = p + p + + """ + + def __init__(self, project, resource, offset): + self.project = project + self.resource = resource + self.offset = offset + self.pymodule = self.project.get_pymodule(self.resource) + scope = self.pymodule.get_scope().get_inner_scope_for_offset(offset) + if scope.get_kind() != 'Function': + raise exceptions.RefactoringError( + 'Introduce parameter should be performed inside functions') + self.pyfunction = scope.pyobject + self.name, self.pyname = self._get_name_and_pyname() + if self.pyname is None: + raise exceptions.RefactoringError( + 'Cannot find the definition of <%s>' % self.name) + + def _get_primary(self): + word_finder = worder.Worder(self.resource.read()) + return word_finder.get_primary_at(self.offset) + + def _get_name_and_pyname(self): + return (worder.get_name_at(self.resource, self.offset), + evaluate.eval_location(self.pymodule, self.offset)) + + def get_changes(self, new_parameter): + definition_info = functionutils.DefinitionInfo.read(self.pyfunction) + definition_info.args_with_defaults.append((new_parameter, + self._get_primary())) + collector = codeanalyze.ChangeCollector(self.resource.read()) + header_start, header_end = self._get_header_offsets() + body_start, body_end = sourceutils.get_body_region(self.pyfunction) + collector.add_change(header_start, header_end, + definition_info.to_string()) + self._change_function_occurances(collector, body_start, + body_end, new_parameter) + changes = rope.base.change.ChangeSet('Introduce parameter <%s>' % + new_parameter) + change = rope.base.change.ChangeContents(self.resource, + collector.get_changed()) + changes.add_change(change) + return changes + + def _get_header_offsets(self): + lines = self.pymodule.lines + start_line = self.pyfunction.get_scope().get_start() + end_line = self.pymodule.logical_lines.\ + logical_line_in(start_line)[1] + start = lines.get_line_start(start_line) + end = lines.get_line_end(end_line) + start = self.pymodule.source_code.find('def', start) + 4 + end = self.pymodule.source_code.rfind(':', start, end) + return start, end + + def _change_function_occurances(self, collector, function_start, + function_end, new_name): + finder = occurrences.create_finder(self.project, self.name, + self.pyname) + for occurrence in finder.find_occurrences(resource=self.resource): + start, end = occurrence.get_primary_range() + if function_start <= start < function_end: + collector.add_change(start, end, new_name) diff --git a/venv/Lib/site-packages/rope/refactor/localtofield.py b/venv/Lib/site-packages/rope/refactor/localtofield.py new file mode 100644 index 0000000000000000000000000000000000000000..f276070f71fab99a7efadeed8e91c2437c201328 --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/localtofield.py @@ -0,0 +1,49 @@ +from rope.base import pynames, evaluate, exceptions, worder +from rope.refactor.rename import Rename + + +class LocalToField(object): + + def __init__(self, project, resource, offset): + self.project = project + self.resource = resource + self.offset = offset + + def get_changes(self): + name = worder.get_name_at(self.resource, self.offset) + this_pymodule = self.project.get_pymodule(self.resource) + pyname = evaluate.eval_location(this_pymodule, self.offset) + if not self._is_a_method_local(pyname): + raise exceptions.RefactoringError( + 'Convert local variable to field should be performed on \n' + 'a local variable of a method.') + + pymodule, lineno = pyname.get_definition_location() + function_scope = pymodule.get_scope().get_inner_scope_for_line(lineno) + # Not checking redefinition + #self._check_redefinition(name, function_scope) + + new_name = self._get_field_name(function_scope.pyobject, name) + changes = Rename(self.project, self.resource, self.offset).\ + get_changes(new_name, resources=[self.resource]) + return changes + + def _check_redefinition(self, name, function_scope): + class_scope = function_scope.parent + if name in class_scope.pyobject: + raise exceptions.RefactoringError( + 'The field %s already exists' % name) + + def _get_field_name(self, pyfunction, name): + self_name = pyfunction.get_param_names()[0] + new_name = self_name + '.' + name + return new_name + + def _is_a_method_local(self, pyname): + pymodule, lineno = pyname.get_definition_location() + holding_scope = pymodule.get_scope().get_inner_scope_for_line(lineno) + parent = holding_scope.parent + return isinstance(pyname, pynames.AssignedName) and \ + pyname in holding_scope.get_names().values() and \ + holding_scope.get_kind() == 'Function' and \ + parent is not None and parent.get_kind() == 'Class' diff --git a/venv/Lib/site-packages/rope/refactor/method_object.py b/venv/Lib/site-packages/rope/refactor/method_object.py new file mode 100644 index 0000000000000000000000000000000000000000..29ce429db6115a389cda2b8adf6b7e0d194324f0 --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/method_object.py @@ -0,0 +1,90 @@ +import warnings + +from rope.base import libutils +from rope.base import pyobjects, exceptions, change, evaluate, codeanalyze +from rope.refactor import sourceutils, occurrences, rename + + +class MethodObject(object): + + def __init__(self, project, resource, offset): + self.project = project + this_pymodule = self.project.get_pymodule(resource) + pyname = evaluate.eval_location(this_pymodule, offset) + if pyname is None or not isinstance(pyname.get_object(), + pyobjects.PyFunction): + raise exceptions.RefactoringError( + 'Replace method with method object refactoring should be ' + 'performed on a function.') + self.pyfunction = pyname.get_object() + self.pymodule = self.pyfunction.get_module() + self.resource = self.pymodule.get_resource() + + def get_new_class(self, name): + body = sourceutils.fix_indentation( + self._get_body(), sourceutils.get_indent(self.project) * 2) + return 'class %s(object):\n\n%s%sdef __call__(self):\n%s' % \ + (name, self._get_init(), + ' ' * sourceutils.get_indent(self.project), body) + + def get_changes(self, classname=None, new_class_name=None): + if new_class_name is not None: + warnings.warn( + 'new_class_name parameter is deprecated; use classname', + DeprecationWarning, stacklevel=2) + classname = new_class_name + collector = codeanalyze.ChangeCollector(self.pymodule.source_code) + start, end = sourceutils.get_body_region(self.pyfunction) + indents = sourceutils.get_indents( + self.pymodule.lines, self.pyfunction.get_scope().get_start()) + \ + sourceutils.get_indent(self.project) + new_contents = ' ' * indents + 'return %s(%s)()\n' % \ + (classname, ', '.join(self._get_parameter_names())) + collector.add_change(start, end, new_contents) + insertion = self._get_class_insertion_point() + collector.add_change(insertion, insertion, + '\n\n' + self.get_new_class(classname)) + changes = change.ChangeSet( + 'Replace method with method object refactoring') + changes.add_change(change.ChangeContents(self.resource, + collector.get_changed())) + return changes + + def _get_class_insertion_point(self): + current = self.pyfunction + while current.parent != self.pymodule: + current = current.parent + end = self.pymodule.lines.get_line_end(current.get_scope().get_end()) + return min(end + 1, len(self.pymodule.source_code)) + + def _get_body(self): + body = sourceutils.get_body(self.pyfunction) + for param in self._get_parameter_names(): + body = param + ' = None\n' + body + pymod = libutils.get_string_module( + self.project, body, self.resource) + pyname = pymod[param] + finder = occurrences.create_finder(self.project, param, pyname) + result = rename.rename_in_module(finder, 'self.' + param, + pymodule=pymod) + body = result[result.index('\n') + 1:] + return body + + def _get_init(self): + params = self._get_parameter_names() + indents = ' ' * sourceutils.get_indent(self.project) + if not params: + return '' + header = indents + 'def __init__(self' + body = '' + for arg in params: + new_name = arg + if arg == 'self': + new_name = 'host' + header += ', %s' % new_name + body += indents * 2 + 'self.%s = %s\n' % (arg, new_name) + header += '):' + return '%s\n%s\n' % (header, body) + + def _get_parameter_names(self): + return self.pyfunction.get_param_names() diff --git a/venv/Lib/site-packages/rope/refactor/move.py b/venv/Lib/site-packages/rope/refactor/move.py new file mode 100644 index 0000000000000000000000000000000000000000..ce618277e52ca594558b0b6f876f676b57eba7b8 --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/move.py @@ -0,0 +1,784 @@ +"""A module containing classes for move refactoring + +`create_move()` is a factory for creating move refactoring objects +based on inputs. + +""" +from rope.base import (pyobjects, codeanalyze, exceptions, pynames, + taskhandle, evaluate, worder, libutils) +from rope.base.change import ChangeSet, ChangeContents, MoveResource +from rope.refactor import importutils, rename, occurrences, sourceutils, \ + functionutils + + +def create_move(project, resource, offset=None): + """A factory for creating Move objects + + Based on `resource` and `offset`, return one of `MoveModule`, + `MoveGlobal` or `MoveMethod` for performing move refactoring. + + """ + if offset is None: + return MoveModule(project, resource) + this_pymodule = project.get_pymodule(resource) + pyname = evaluate.eval_location(this_pymodule, offset) + if pyname is not None: + pyobject = pyname.get_object() + if isinstance(pyobject, pyobjects.PyModule) or \ + isinstance(pyobject, pyobjects.PyPackage): + return MoveModule(project, pyobject.get_resource()) + if isinstance(pyobject, pyobjects.PyFunction) and \ + isinstance(pyobject.parent, pyobjects.PyClass): + return MoveMethod(project, resource, offset) + if isinstance(pyobject, pyobjects.PyDefinedObject) and \ + isinstance(pyobject.parent, pyobjects.PyModule) or \ + isinstance(pyname, pynames.AssignedName): + return MoveGlobal(project, resource, offset) + raise exceptions.RefactoringError( + 'Move only works on global classes/functions/variables, modules and ' + 'methods.') + + +class MoveMethod(object): + """For moving methods + + It makes a new method in the destination class and changes + the body of the old method to call the new method. You can + inline the old method to change all of its occurrences. + + """ + + def __init__(self, project, resource, offset): + self.project = project + this_pymodule = self.project.get_pymodule(resource) + pyname = evaluate.eval_location(this_pymodule, offset) + self.method_name = worder.get_name_at(resource, offset) + self.pyfunction = pyname.get_object() + if self.pyfunction.get_kind() != 'method': + raise exceptions.RefactoringError('Only normal methods' + ' can be moved.') + + def get_changes(self, dest_attr, new_name=None, resources=None, + task_handle=taskhandle.NullTaskHandle()): + """Return the changes needed for this refactoring + + Parameters: + + - `dest_attr`: the name of the destination attribute + - `new_name`: the name of the new method; if `None` uses + the old name + - `resources` can be a list of `rope.base.resources.File`\s to + apply this refactoring on. If `None`, the restructuring + will be applied to all python files. + + """ + changes = ChangeSet('Moving method <%s>' % self.method_name) + if resources is None: + resources = self.project.get_python_files() + if new_name is None: + new_name = self.get_method_name() + resource1, start1, end1, new_content1 = \ + self._get_changes_made_by_old_class(dest_attr, new_name) + collector1 = codeanalyze.ChangeCollector(resource1.read()) + collector1.add_change(start1, end1, new_content1) + + resource2, start2, end2, new_content2 = \ + self._get_changes_made_by_new_class(dest_attr, new_name) + if resource1 == resource2: + collector1.add_change(start2, end2, new_content2) + else: + collector2 = codeanalyze.ChangeCollector(resource2.read()) + collector2.add_change(start2, end2, new_content2) + result = collector2.get_changed() + import_tools = importutils.ImportTools(self.project) + new_imports = self._get_used_imports(import_tools) + if new_imports: + goal_pymodule = libutils.get_string_module( + self.project, result, resource2) + result = _add_imports_to_module( + import_tools, goal_pymodule, new_imports) + if resource2 in resources: + changes.add_change(ChangeContents(resource2, result)) + + if resource1 in resources: + changes.add_change(ChangeContents(resource1, + collector1.get_changed())) + return changes + + def get_method_name(self): + return self.method_name + + def _get_used_imports(self, import_tools): + return importutils.get_imports(self.project, self.pyfunction) + + def _get_changes_made_by_old_class(self, dest_attr, new_name): + pymodule = self.pyfunction.get_module() + indents = self._get_scope_indents(self.pyfunction) + body = 'return self.%s.%s(%s)\n' % ( + dest_attr, new_name, self._get_passed_arguments_string()) + region = sourceutils.get_body_region(self.pyfunction) + return (pymodule.get_resource(), region[0], region[1], + sourceutils.fix_indentation(body, indents)) + + def _get_scope_indents(self, pyobject): + pymodule = pyobject.get_module() + return sourceutils.get_indents( + pymodule.lines, pyobject.get_scope().get_start()) + \ + sourceutils.get_indent(self.project) + + def _get_changes_made_by_new_class(self, dest_attr, new_name): + old_pyclass = self.pyfunction.parent + if dest_attr not in old_pyclass: + raise exceptions.RefactoringError( + 'Destination attribute <%s> not found' % dest_attr) + pyclass = old_pyclass[dest_attr].get_object().get_type() + if not isinstance(pyclass, pyobjects.PyClass): + raise exceptions.RefactoringError( + 'Unknown class type for attribute <%s>' % dest_attr) + pymodule = pyclass.get_module() + resource = pyclass.get_module().get_resource() + start, end = sourceutils.get_body_region(pyclass) + pre_blanks = '\n' + if pymodule.source_code[start:end].strip() != 'pass': + pre_blanks = '\n\n' + start = end + indents = self._get_scope_indents(pyclass) + body = pre_blanks + sourceutils.fix_indentation( + self.get_new_method(new_name), indents) + return resource, start, end, body + + def get_new_method(self, name): + return '%s\n%s' % ( + self._get_new_header(name), + sourceutils.fix_indentation(self._get_body(), + sourceutils.get_indent(self.project))) + + def _get_unchanged_body(self): + return sourceutils.get_body(self.pyfunction) + + def _get_body(self, host='host'): + self_name = self._get_self_name() + body = self_name + ' = None\n' + self._get_unchanged_body() + pymodule = libutils.get_string_module(self.project, body) + finder = occurrences.create_finder( + self.project, self_name, pymodule[self_name]) + result = rename.rename_in_module(finder, host, pymodule=pymodule) + if result is None: + result = body + return result[result.index('\n') + 1:] + + def _get_self_name(self): + return self.pyfunction.get_param_names()[0] + + def _get_new_header(self, name): + header = 'def %s(self' % name + if self._is_host_used(): + header += ', host' + definition_info = functionutils.DefinitionInfo.read(self.pyfunction) + others = definition_info.arguments_to_string(1) + if others: + header += ', ' + others + return header + '):' + + def _get_passed_arguments_string(self): + result = '' + if self._is_host_used(): + result = 'self' + definition_info = functionutils.DefinitionInfo.read(self.pyfunction) + others = definition_info.arguments_to_string(1) + if others: + if result: + result += ', ' + result += others + return result + + def _is_host_used(self): + return self._get_body('__old_self') != self._get_unchanged_body() + + +class MoveGlobal(object): + """For moving global function and classes""" + + def __init__(self, project, resource, offset): + self.project = project + this_pymodule = self.project.get_pymodule(resource) + self.old_pyname = evaluate.eval_location(this_pymodule, offset) + if self.old_pyname is None: + raise exceptions.RefactoringError( + 'Move refactoring should be performed on a ' + 'class/function/variable.') + if self._is_variable(self.old_pyname): + self.old_name = worder.get_name_at(resource, offset) + pymodule = this_pymodule + else: + self.old_name = self.old_pyname.get_object().get_name() + pymodule = self.old_pyname.get_object().get_module() + self._check_exceptional_conditions() + self.source = pymodule.get_resource() + self.tools = _MoveTools(self.project, self.source, + self.old_pyname, self.old_name) + self.import_tools = self.tools.import_tools + + def _import_filter(self, stmt): + module_name = libutils.modname(self.source) + + if isinstance(stmt.import_info, importutils.NormalImport): + # Affect any statement that imports the source module + return any(module_name == name + for name, alias in stmt.import_info.names_and_aliases) + elif isinstance(stmt.import_info, importutils.FromImport): + # Affect statements importing from the source package + if '.' in module_name: + package_name, basename = module_name.rsplit('.', 1) + if (stmt.import_info.module_name == package_name and + any(basename == name + for name, alias in stmt.import_info.names_and_aliases)): + return True + return stmt.import_info.module_name == module_name + return False + + def _check_exceptional_conditions(self): + if self._is_variable(self.old_pyname): + pymodule = self.old_pyname.get_definition_location()[0] + try: + pymodule.get_scope().get_name(self.old_name) + except exceptions.NameNotFoundError: + self._raise_refactoring_error() + elif not (isinstance(self.old_pyname.get_object(), + pyobjects.PyDefinedObject) and + self._is_global(self.old_pyname.get_object())): + self._raise_refactoring_error() + + def _raise_refactoring_error(self): + raise exceptions.RefactoringError( + 'Move refactoring should be performed on a global class, function ' + 'or variable.') + + def _is_global(self, pyobject): + return pyobject.get_scope().parent == pyobject.get_module().get_scope() + + def _is_variable(self, pyname): + return isinstance(pyname, pynames.AssignedName) + + def get_changes(self, dest, resources=None, + task_handle=taskhandle.NullTaskHandle()): + if resources is None: + resources = self.project.get_python_files() + if dest is None or not dest.exists(): + raise exceptions.RefactoringError( + 'Move destination does not exist.') + if dest.is_folder() and dest.has_child('__init__.py'): + dest = dest.get_child('__init__.py') + if dest.is_folder(): + raise exceptions.RefactoringError( + 'Move destination for non-modules should not be folders.') + if self.source == dest: + raise exceptions.RefactoringError( + 'Moving global elements to the same module.') + return self._calculate_changes(dest, resources, task_handle) + + def _calculate_changes(self, dest, resources, task_handle): + changes = ChangeSet('Moving global <%s>' % self.old_name) + job_set = task_handle.create_jobset('Collecting Changes', + len(resources)) + for file_ in resources: + job_set.started_job(file_.path) + if file_ == self.source: + changes.add_change(self._source_module_changes(dest)) + elif file_ == dest: + changes.add_change(self._dest_module_changes(dest)) + elif self.tools.occurs_in_module(resource=file_): + pymodule = self.project.get_pymodule(file_) + # Changing occurrences + placeholder = '__rope_renaming_%s_' % self.old_name + source = self.tools.rename_in_module(placeholder, + resource=file_) + should_import = source is not None + # Removing out of date imports + pymodule = self.tools.new_pymodule(pymodule, source) + source = self.import_tools.organize_imports( + pymodule, sort=False, import_filter=self._import_filter) + # Adding new import + if should_import: + pymodule = self.tools.new_pymodule(pymodule, source) + source, imported = importutils.add_import( + self.project, pymodule, self._new_modname(dest), + self.old_name) + source = source.replace(placeholder, imported) + source = self.tools.new_source(pymodule, source) + if source != file_.read(): + changes.add_change(ChangeContents(file_, source)) + job_set.finished_job() + return changes + + def _source_module_changes(self, dest): + placeholder = '__rope_moving_%s_' % self.old_name + handle = _ChangeMoveOccurrencesHandle(placeholder) + occurrence_finder = occurrences.create_finder( + self.project, self.old_name, self.old_pyname) + start, end = self._get_moving_region() + renamer = ModuleSkipRenamer(occurrence_finder, self.source, + handle, start, end) + source = renamer.get_changed_module() + pymodule = libutils.get_string_module(self.project, source, self.source) + source = self.import_tools.organize_imports(pymodule, sort=False) + if handle.occurred: + pymodule = libutils.get_string_module( + self.project, source, self.source) + # Adding new import + source, imported = importutils.add_import( + self.project, pymodule, self._new_modname(dest), self.old_name) + source = source.replace(placeholder, imported) + return ChangeContents(self.source, source) + + def _new_modname(self, dest): + return libutils.modname(dest) + + def _dest_module_changes(self, dest): + # Changing occurrences + pymodule = self.project.get_pymodule(dest) + source = self.tools.rename_in_module(self.old_name, pymodule) + pymodule = self.tools.new_pymodule(pymodule, source) + + moving, imports = self._get_moving_element_with_imports() + pymodule, has_changed = self._add_imports2(pymodule, imports) + + module_with_imports = self.import_tools.module_imports(pymodule) + source = pymodule.source_code + lineno = 0 + if module_with_imports.imports: + lineno = module_with_imports.imports[-1].end_line - 1 + else: + while lineno < pymodule.lines.length() and \ + pymodule.lines.get_line(lineno + 1).\ + lstrip().startswith('#'): + lineno += 1 + if lineno > 0: + cut = pymodule.lines.get_line_end(lineno) + 1 + result = source[:cut] + '\n\n' + moving + source[cut:] + else: + result = moving + source + + # Organizing imports + source = result + pymodule = libutils.get_string_module(self.project, source, dest) + source = self.import_tools.organize_imports(pymodule, sort=False, + unused=False) + # Remove unused imports of the old module + pymodule = libutils.get_string_module(self.project, source, dest) + source = self.import_tools.organize_imports( + pymodule, sort=False, selfs=False, unused=True, + import_filter=self._import_filter) + return ChangeContents(dest, source) + + def _get_moving_element_with_imports(self): + return moving_code_with_imports( + self.project, self.source, self._get_moving_element()) + + def _get_module_with_imports(self, source_code, resource): + pymodule = libutils.get_string_module( + self.project, source_code, resource) + return self.import_tools.module_imports(pymodule) + + def _get_moving_element(self): + start, end = self._get_moving_region() + moving = self.source.read()[start:end] + return moving.rstrip() + '\n' + + def _get_moving_region(self): + pymodule = self.project.get_pymodule(self.source) + lines = pymodule.lines + if self._is_variable(self.old_pyname): + logical_lines = pymodule.logical_lines + lineno = logical_lines.logical_line_in( + self.old_pyname.get_definition_location()[1])[0] + start = lines.get_line_start(lineno) + end_line = logical_lines.logical_line_in(lineno)[1] + else: + scope = self.old_pyname.get_object().get_scope() + start = lines.get_line_start(scope.get_start()) + end_line = scope.get_end() + + # Include comment lines before the definition + start_line = lines.get_line_number(start) + while start_line > 1 and lines.get_line(start_line - 1).startswith('#'): + start_line -= 1 + start = lines.get_line_start(start_line) + + while end_line < lines.length() and \ + lines.get_line(end_line + 1).strip() == '': + end_line += 1 + end = min(lines.get_line_end(end_line) + 1, len(pymodule.source_code)) + return start, end + + def _add_imports2(self, pymodule, new_imports): + source = self.tools.add_imports(pymodule, new_imports) + if source is None: + return pymodule, False + else: + resource = pymodule.get_resource() + pymodule = libutils.get_string_module( + self.project, source, resource) + return pymodule, True + + +class MoveModule(object): + """For moving modules and packages""" + + def __init__(self, project, resource): + self.project = project + if not resource.is_folder() and resource.name == '__init__.py': + resource = resource.parent + if resource.is_folder() and not resource.has_child('__init__.py'): + raise exceptions.RefactoringError( + 'Cannot move non-package folder.') + dummy_pymodule = libutils.get_string_module(self.project, '') + self.old_pyname = pynames.ImportedModule(dummy_pymodule, + resource=resource) + self.source = self.old_pyname.get_object().get_resource() + if self.source.is_folder(): + self.old_name = self.source.name + else: + self.old_name = self.source.name[:-3] + self.tools = _MoveTools(self.project, self.source, + self.old_pyname, self.old_name) + self.import_tools = self.tools.import_tools + + def get_changes(self, dest, resources=None, + task_handle=taskhandle.NullTaskHandle()): + if resources is None: + resources = self.project.get_python_files() + if dest is None or not dest.is_folder(): + raise exceptions.RefactoringError( + 'Move destination for modules should be packages.') + return self._calculate_changes(dest, resources, task_handle) + + def _calculate_changes(self, dest, resources, task_handle): + changes = ChangeSet('Moving module <%s>' % self.old_name) + job_set = task_handle.create_jobset('Collecting changes', + len(resources)) + for module in resources: + job_set.started_job(module.path) + if module == self.source: + self._change_moving_module(changes, dest) + else: + source = self._change_occurrences_in_module(dest, + resource=module) + if source is not None: + changes.add_change(ChangeContents(module, source)) + job_set.finished_job() + if self.project == self.source.project: + changes.add_change(MoveResource(self.source, dest.path)) + return changes + + def _new_modname(self, dest): + destname = libutils.modname(dest) + if destname: + return destname + '.' + self.old_name + return self.old_name + + def _new_import(self, dest): + return importutils.NormalImport([(self._new_modname(dest), None)]) + + def _change_moving_module(self, changes, dest): + if not self.source.is_folder(): + pymodule = self.project.get_pymodule(self.source) + source = self.import_tools.relatives_to_absolutes(pymodule) + pymodule = self.tools.new_pymodule(pymodule, source) + source = self._change_occurrences_in_module(dest, pymodule) + source = self.tools.new_source(pymodule, source) + if source != self.source.read(): + changes.add_change(ChangeContents(self.source, source)) + + def _change_occurrences_in_module(self, dest, pymodule=None, + resource=None): + if not self.tools.occurs_in_module(pymodule=pymodule, + resource=resource): + return + if pymodule is None: + pymodule = self.project.get_pymodule(resource) + new_name = self._new_modname(dest) + module_imports = importutils.get_module_imports(self.project, pymodule) + changed = False + source = None + if libutils.modname(dest): + changed = self._change_import_statements(dest, new_name, + module_imports) + if changed: + source = module_imports.get_changed_source() + source = self.tools.new_source(pymodule, source) + pymodule = self.tools.new_pymodule(pymodule, source) + + new_import = self._new_import(dest) + source = self.tools.rename_in_module( + new_name, imports=True, pymodule=pymodule, + resource=resource if not changed else None) + should_import = self.tools.occurs_in_module( + pymodule=pymodule, resource=resource, imports=False) + pymodule = self.tools.new_pymodule(pymodule, source) + source = self.tools.remove_old_imports(pymodule) + if should_import: + pymodule = self.tools.new_pymodule(pymodule, source) + source = self.tools.add_imports(pymodule, [new_import]) + source = self.tools.new_source(pymodule, source) + if source is not None and source != pymodule.resource.read(): + return source + return None + + def _change_import_statements(self, dest, new_name, module_imports): + moving_module = self.source + parent_module = moving_module.parent + + changed = False + for import_stmt in module_imports.imports: + if not any(name_and_alias[0] == self.old_name + for name_and_alias in + import_stmt.import_info.names_and_aliases) and \ + not any(name_and_alias[0] == libutils.modname(self.source) + for name_and_alias in + import_stmt.import_info.names_and_aliases): + continue + + # Case 1: Look for normal imports of the moving module. + if isinstance(import_stmt.import_info, importutils.NormalImport): + continue + + # Case 2: The moving module is from-imported. + changed = self._handle_moving_in_from_import_stmt( + dest, import_stmt, module_imports, parent_module) or changed + + # Case 3: Names are imported from the moving module. + context = importutils.importinfo.ImportContext(self.project, None) + if not import_stmt.import_info.is_empty() and \ + import_stmt.import_info.get_imported_resource(context) == \ + moving_module: + import_stmt.import_info = importutils.FromImport( + new_name, import_stmt.import_info.level, + import_stmt.import_info.names_and_aliases) + changed = True + + return changed + + def _handle_moving_in_from_import_stmt(self, dest, import_stmt, + module_imports, parent_module): + changed = False + context = importutils.importinfo.ImportContext(self.project, None) + if import_stmt.import_info.get_imported_resource(context) == \ + parent_module: + imports = import_stmt.import_info.names_and_aliases + new_imports = [] + for name, alias in imports: + # The moving module was imported. + if name == self.old_name: + changed = True + new_import = importutils.FromImport( + libutils.modname(dest), 0, + [(self.old_name, alias)]) + module_imports.add_import(new_import) + else: + new_imports.append((name, alias)) + + # Update the imports if the imported names were changed. + if new_imports != imports: + changed = True + if new_imports: + import_stmt.import_info = importutils.FromImport( + import_stmt.import_info.module_name, + import_stmt.import_info.level, + new_imports) + else: + import_stmt.empty_import() + return changed + + +class _ChangeMoveOccurrencesHandle(object): + + def __init__(self, new_name): + self.new_name = new_name + self.occurred = False + + def occurred_inside_skip(self, change_collector, occurrence): + pass + + def occurred_outside_skip(self, change_collector, occurrence): + start, end = occurrence.get_primary_range() + change_collector.add_change(start, end, self.new_name) + self.occurred = True + + +class _MoveTools(object): + + def __init__(self, project, source, pyname, old_name): + self.project = project + self.source = source + self.old_pyname = pyname + self.old_name = old_name + self.import_tools = importutils.ImportTools(self.project) + + def remove_old_imports(self, pymodule): + old_source = pymodule.source_code + module_with_imports = self.import_tools.module_imports(pymodule) + + class CanSelect(object): + changed = False + old_name = self.old_name + old_pyname = self.old_pyname + + def __call__(self, name): + try: + if name == self.old_name and \ + pymodule[name].get_object() == \ + self.old_pyname.get_object(): + self.changed = True + return False + except exceptions.AttributeNotFoundError: + pass + return True + can_select = CanSelect() + module_with_imports.filter_names(can_select) + new_source = module_with_imports.get_changed_source() + if old_source != new_source: + return new_source + + def rename_in_module(self, new_name, pymodule=None, + imports=False, resource=None): + occurrence_finder = self._create_finder(imports) + source = rename.rename_in_module( + occurrence_finder, new_name, replace_primary=True, + pymodule=pymodule, resource=resource) + return source + + def occurs_in_module(self, pymodule=None, resource=None, imports=True): + finder = self._create_finder(imports) + for occurrence in finder.find_occurrences(pymodule=pymodule, + resource=resource): + return True + return False + + def _create_finder(self, imports): + return occurrences.create_finder(self.project, self.old_name, + self.old_pyname, imports=imports, + keywords=False) + + def new_pymodule(self, pymodule, source): + if source is not None: + return libutils.get_string_module( + self.project, source, pymodule.get_resource()) + return pymodule + + def new_source(self, pymodule, source): + if source is None: + return pymodule.source_code + return source + + def add_imports(self, pymodule, new_imports): + return _add_imports_to_module(self.import_tools, pymodule, new_imports) + + +def _add_imports_to_module(import_tools, pymodule, new_imports): + module_with_imports = import_tools.module_imports(pymodule) + for new_import in new_imports: + module_with_imports.add_import(new_import) + return module_with_imports.get_changed_source() + + +def moving_code_with_imports(project, resource, source): + import_tools = importutils.ImportTools(project) + pymodule = libutils.get_string_module(project, source, resource) + + # Strip comment prefix, if any. These need to stay before the moving + # section, but imports would be added between them. + lines = codeanalyze.SourceLinesAdapter(source) + start = 1 + while start < lines.length() and lines.get_line(start).startswith('#'): + start += 1 + moving_prefix = source[:lines.get_line_start(start)] + pymodule = libutils.get_string_module( + project, source[lines.get_line_start(start):], resource) + + origin = project.get_pymodule(resource) + + imports = [] + for stmt in import_tools.module_imports(origin).imports: + imports.append(stmt.import_info) + + back_names = [] + for name in origin: + if name not in pymodule: + back_names.append(name) + imports.append(import_tools.get_from_import(resource, back_names)) + + source = _add_imports_to_module(import_tools, pymodule, imports) + pymodule = libutils.get_string_module(project, source, resource) + + source = import_tools.relatives_to_absolutes(pymodule) + pymodule = libutils.get_string_module(project, source, resource) + source = import_tools.organize_imports(pymodule, selfs=False) + pymodule = libutils.get_string_module(project, source, resource) + + # extracting imports after changes + module_imports = import_tools.module_imports(pymodule) + imports = [import_stmt.import_info + for import_stmt in module_imports.imports] + start = 1 + if module_imports.imports: + start = module_imports.imports[-1].end_line + lines = codeanalyze.SourceLinesAdapter(source) + while start < lines.length() and not lines.get_line(start).strip(): + start += 1 + + # Reinsert the prefix which was removed at the beginning + moving = moving_prefix + source[lines.get_line_start(start):] + return moving, imports + + +class ModuleSkipRenamerHandle(object): + + def occurred_outside_skip(self, change_collector, occurrence): + pass + + def occurred_inside_skip(self, change_collector, occurrence): + pass + + +class ModuleSkipRenamer(object): + """Rename occurrences in a module + + This class can be used when you want to treat a region in a file + separately from other parts when renaming. + + """ + + def __init__(self, occurrence_finder, resource, handle=None, + skip_start=0, skip_end=0, replacement=''): + """Constructor + + if replacement is `None` the region is not changed. Otherwise + it is replaced with `replacement`. + + """ + self.occurrence_finder = occurrence_finder + self.resource = resource + self.skip_start = skip_start + self.skip_end = skip_end + self.replacement = replacement + self.handle = handle + if self.handle is None: + self.handle = ModuleSkipRenamerHandle() + + def get_changed_module(self): + source = self.resource.read() + change_collector = codeanalyze.ChangeCollector(source) + if self.replacement is not None: + change_collector.add_change(self.skip_start, self.skip_end, + self.replacement) + for occurrence in self.occurrence_finder.find_occurrences( + self.resource): + start, end = occurrence.get_primary_range() + if self.skip_start <= start < self.skip_end: + self.handle.occurred_inside_skip(change_collector, occurrence) + else: + self.handle.occurred_outside_skip(change_collector, occurrence) + result = change_collector.get_changed() + if result is not None and result != source: + return result diff --git a/venv/Lib/site-packages/rope/refactor/multiproject.py b/venv/Lib/site-packages/rope/refactor/multiproject.py new file mode 100644 index 0000000000000000000000000000000000000000..ac243bdafcea6a7fd01cbe547a4a94ac04dfa7e5 --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/multiproject.py @@ -0,0 +1,78 @@ +"""This module can be used for performing cross-project refactorings + +See the "cross-project refactorings" section of ``docs/library.rst`` +file. + +""" + +from rope.base import resources, libutils + + +class MultiProjectRefactoring(object): + + def __init__(self, refactoring, projects, addpath=True): + """Create a multiproject proxy for the main refactoring + + `projects` are other project. + + """ + self.refactoring = refactoring + self.projects = projects + self.addpath = addpath + + def __call__(self, project, *args, **kwds): + """Create the refactoring""" + return _MultiRefactoring(self.refactoring, self.projects, + self.addpath, project, *args, **kwds) + + +class _MultiRefactoring(object): + + def __init__(self, refactoring, other_projects, addpath, + project, *args, **kwds): + self.refactoring = refactoring + self.projects = [project] + other_projects + for other_project in other_projects: + for folder in self.project.get_source_folders(): + other_project.get_prefs().add('python_path', folder.real_path) + self.refactorings = [] + for other in self.projects: + args, kwds = self._resources_for_args(other, args, kwds) + self.refactorings.append( + self.refactoring(other, *args, **kwds)) + + def get_all_changes(self, *args, **kwds): + """Get a project to changes dict""" + result = [] + for project, refactoring in zip(self.projects, self.refactorings): + args, kwds = self._resources_for_args(project, args, kwds) + result.append((project, refactoring.get_changes(*args, **kwds))) + return result + + def __getattr__(self, name): + return getattr(self.main_refactoring, name) + + def _resources_for_args(self, project, args, kwds): + newargs = [self._change_project_resource(project, arg) for arg in args] + newkwds = dict((name, self._change_project_resource(project, value)) + for name, value in kwds.items()) + return newargs, newkwds + + def _change_project_resource(self, project, obj): + if isinstance(obj, resources.Resource) and \ + obj.project != project: + return libutils.path_to_resource(project, obj.real_path) + return obj + + @property + def project(self): + return self.projects[0] + + @property + def main_refactoring(self): + return self.refactorings[0] + + +def perform(project_changes): + for project, changes in project_changes: + project.do(changes) diff --git a/venv/Lib/site-packages/rope/refactor/occurrences.py b/venv/Lib/site-packages/rope/refactor/occurrences.py new file mode 100644 index 0000000000000000000000000000000000000000..dfc2d685d7d858b5b6280f62a916ae6b94954577 --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/occurrences.py @@ -0,0 +1,402 @@ +"""Find occurrences of a name in a project. + +This module consists of a `Finder` that finds all occurrences of a name +in a project. The `Finder.find_occurrences()` method is a generator that +yields `Occurrence` instances for each occurrence of the name. To create +a `Finder` object, use the `create_finder()` function: + + finder = occurrences.create_finder(project, 'foo', pyname) + for occurrence in finder.find_occurrences(): + pass + +It's possible to filter the occurrences. They can be specified when +calling the `create_finder()` function. + + * `only_calls`: If True, return only those instances where the name is + a function that's being called. + + * `imports`: If False, don't return instances that are in import + statements. + + * `unsure`: If a prediate function, return instances where we don't + know what the name references. It also filters based on the + predicate function. + + * `docs`: If True, it will search for occurrences in regions normally + ignored. E.g., strings and comments. + + * `in_hierarchy`: If True, it will find occurrences if the name is in + the class's hierarchy. + + * `instance`: Used only when you want implicit interfaces to be + considered. + + * `keywords`: If False, don't return instances that are the names of keyword + arguments +""" + +import re + +from rope.base import codeanalyze +from rope.base import evaluate +from rope.base import exceptions +from rope.base import pynames +from rope.base import pyobjects +from rope.base import utils +from rope.base import worder + + +class Finder(object): + """For finding occurrences of a name + + The constructor takes a `filters` argument. It should be a list + of functions that take a single argument. For each possible + occurrence, these functions are called in order with the an + instance of `Occurrence`: + + * If it returns `None` other filters are tried. + * If it returns `True`, the occurrence will be a match. + * If it returns `False`, the occurrence will be skipped. + * If all of the filters return `None`, it is skipped also. + + """ + + def __init__(self, project, name, filters=[lambda o: True], docs=False): + self.project = project + self.name = name + self.docs = docs + self.filters = filters + self._textual_finder = _TextualFinder(name, docs=docs) + + def find_occurrences(self, resource=None, pymodule=None): + """Generate `Occurrence` instances""" + tools = _OccurrenceToolsCreator(self.project, resource=resource, + pymodule=pymodule, docs=self.docs) + for offset in self._textual_finder.find_offsets(tools.source_code): + occurrence = Occurrence(tools, offset) + for filter in self.filters: + result = filter(occurrence) + if result is None: + continue + if result: + yield occurrence + break + + +def create_finder(project, name, pyname, only_calls=False, imports=True, + unsure=None, docs=False, instance=None, in_hierarchy=False, + keywords=True): + """A factory for `Finder` + + Based on the arguments it creates a list of filters. `instance` + argument is needed only when you want implicit interfaces to be + considered. + + """ + pynames_ = set([pyname]) + filters = [] + if only_calls: + filters.append(CallsFilter()) + if not imports: + filters.append(NoImportsFilter()) + if not keywords: + filters.append(NoKeywordsFilter()) + if isinstance(instance, pynames.ParameterName): + for pyobject in instance.get_objects(): + try: + pynames_.add(pyobject[name]) + except exceptions.AttributeNotFoundError: + pass + for pyname in pynames_: + filters.append(PyNameFilter(pyname)) + if in_hierarchy: + filters.append(InHierarchyFilter(pyname)) + if unsure: + filters.append(UnsureFilter(unsure)) + return Finder(project, name, filters=filters, docs=docs) + + +class Occurrence(object): + + def __init__(self, tools, offset): + self.tools = tools + self.offset = offset + self.resource = tools.resource + + @utils.saveit + def get_word_range(self): + return self.tools.word_finder.get_word_range(self.offset) + + @utils.saveit + def get_primary_range(self): + return self.tools.word_finder.get_primary_range(self.offset) + + @utils.saveit + def get_pyname(self): + try: + return self.tools.name_finder.get_pyname_at(self.offset) + except exceptions.BadIdentifierError: + pass + + @utils.saveit + def get_primary_and_pyname(self): + try: + return self.tools.name_finder.get_primary_and_pyname_at( + self.offset) + except exceptions.BadIdentifierError: + pass + + @utils.saveit + def is_in_import_statement(self): + return (self.tools.word_finder.is_from_statement(self.offset) or + self.tools.word_finder.is_import_statement(self.offset)) + + def is_called(self): + return self.tools.word_finder.is_a_function_being_called(self.offset) + + def is_defined(self): + return self.tools.word_finder.is_a_class_or_function_name_in_header( + self.offset) + + def is_a_fixed_primary(self): + return self.tools.word_finder.is_a_class_or_function_name_in_header( + self.offset) or \ + self.tools.word_finder.is_a_name_after_from_import(self.offset) + + def is_written(self): + return self.tools.word_finder.is_assigned_here(self.offset) + + def is_unsure(self): + return unsure_pyname(self.get_pyname()) + + def is_function_keyword_parameter(self): + return self.tools.word_finder.is_function_keyword_parameter( + self.offset) + + @property + @utils.saveit + def lineno(self): + offset = self.get_word_range()[0] + return self.tools.pymodule.lines.get_line_number(offset) + + +def same_pyname(expected, pyname): + """Check whether `expected` and `pyname` are the same""" + if expected is None or pyname is None: + return False + if expected == pyname: + return True + if type(expected) not in (pynames.ImportedModule, pynames.ImportedName) \ + and type(pyname) not in \ + (pynames.ImportedModule, pynames.ImportedName): + return False + return expected.get_definition_location() == \ + pyname.get_definition_location() and \ + expected.get_object() == pyname.get_object() + + +def unsure_pyname(pyname, unbound=True): + """Return `True` if we don't know what this name references""" + if pyname is None: + return True + if unbound and not isinstance(pyname, pynames.UnboundName): + return False + if pyname.get_object() == pyobjects.get_unknown(): + return True + + +class PyNameFilter(object): + """For finding occurrences of a name.""" + + def __init__(self, pyname): + self.pyname = pyname + + def __call__(self, occurrence): + if same_pyname(self.pyname, occurrence.get_pyname()): + return True + + +class InHierarchyFilter(object): + """Finds the occurrence if the name is in the class's hierarchy.""" + + def __init__(self, pyname, implementations_only=False): + self.pyname = pyname + self.impl_only = implementations_only + self.pyclass = self._get_containing_class(pyname) + if self.pyclass is not None: + self.name = pyname.get_object().get_name() + self.roots = self._get_root_classes(self.pyclass, self.name) + else: + self.roots = None + + def __call__(self, occurrence): + if self.roots is None: + return + pyclass = self._get_containing_class(occurrence.get_pyname()) + if pyclass is not None: + roots = self._get_root_classes(pyclass, self.name) + if self.roots.intersection(roots): + return True + + def _get_containing_class(self, pyname): + if isinstance(pyname, pynames.DefinedName): + scope = pyname.get_object().get_scope() + parent = scope.parent + if parent is not None and parent.get_kind() == 'Class': + return parent.pyobject + + def _get_root_classes(self, pyclass, name): + if self.impl_only and pyclass == self.pyclass: + return set([pyclass]) + result = set() + for superclass in pyclass.get_superclasses(): + if name in superclass: + result.update(self._get_root_classes(superclass, name)) + if not result: + return set([pyclass]) + return result + + +class UnsureFilter(object): + """Occurrences where we don't knoow what the name references.""" + + def __init__(self, unsure): + self.unsure = unsure + + def __call__(self, occurrence): + if occurrence.is_unsure() and self.unsure(occurrence): + return True + + +class NoImportsFilter(object): + """Don't include import statements as occurrences.""" + + def __call__(self, occurrence): + if occurrence.is_in_import_statement(): + return False + + +class CallsFilter(object): + """Filter out non-call occurrences.""" + + def __call__(self, occurrence): + if not occurrence.is_called(): + return False + + +class NoKeywordsFilter(object): + """Filter out keyword parameters.""" + + def __call__(self, occurrence): + if occurrence.is_function_keyword_parameter(): + return False + + +class _TextualFinder(object): + + def __init__(self, name, docs=False): + self.name = name + self.docs = docs + self.comment_pattern = _TextualFinder.any('comment', [r'#[^\n]*']) + self.string_pattern = _TextualFinder.any( + 'string', [codeanalyze.get_string_pattern()]) + self.pattern = self._get_occurrence_pattern(self.name) + + def find_offsets(self, source): + if not self._fast_file_query(source): + return + if self.docs: + searcher = self._normal_search + else: + searcher = self._re_search + for matched in searcher(source): + yield matched + + def _re_search(self, source): + for match in self.pattern.finditer(source): + for key, value in match.groupdict().items(): + if value and key == 'occurrence': + yield match.start(key) + + def _normal_search(self, source): + current = 0 + while True: + try: + found = source.index(self.name, current) + current = found + len(self.name) + if (found == 0 or + not self._is_id_char(source[found - 1])) and \ + (current == len(source) or + not self._is_id_char(source[current])): + yield found + except ValueError: + break + + def _is_id_char(self, c): + return c.isalnum() or c == '_' + + def _fast_file_query(self, source): + try: + source.index(self.name) + return True + except ValueError: + return False + + def _get_source(self, resource, pymodule): + if resource is not None: + return resource.read() + else: + return pymodule.source_code + + def _get_occurrence_pattern(self, name): + occurrence_pattern = _TextualFinder.any('occurrence', + ['\\b' + name + '\\b']) + pattern = re.compile(occurrence_pattern + '|' + self.comment_pattern + + '|' + self.string_pattern) + return pattern + + @staticmethod + def any(name, list_): + return '(?P<%s>' % name + '|'.join(list_) + ')' + + +class _OccurrenceToolsCreator(object): + + def __init__(self, project, resource=None, pymodule=None, docs=False): + self.project = project + self.__resource = resource + self.__pymodule = pymodule + self.docs = docs + + @property + @utils.saveit + def name_finder(self): + return evaluate.ScopeNameFinder(self.pymodule) + + @property + @utils.saveit + def source_code(self): + if self.__resource is not None: + return self.resource.read() + else: + return self.pymodule.source_code + + @property + @utils.saveit + def word_finder(self): + return worder.Worder(self.source_code, self.docs) + + @property + @utils.saveit + def resource(self): + if self.__resource is not None: + return self.__resource + if self.__pymodule is not None: + return self.__pymodule.resource + + @property + @utils.saveit + def pymodule(self): + if self.__pymodule is not None: + return self.__pymodule + return self.project.get_pymodule(self.resource) diff --git a/venv/Lib/site-packages/rope/refactor/patchedast.py b/venv/Lib/site-packages/rope/refactor/patchedast.py new file mode 100644 index 0000000000000000000000000000000000000000..0e0522dd98676b1a671f32722c68d29748b1cbc4 --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/patchedast.py @@ -0,0 +1,852 @@ +import collections +import re +import warnings + +from rope.base import ast, codeanalyze, exceptions +from rope.base.utils import pycompat + +try: + basestring +except NameError: + basestring = (str, bytes) + + +def get_patched_ast(source, sorted_children=False): + """Adds ``region`` and ``sorted_children`` fields to nodes + + Adds ``sorted_children`` field only if `sorted_children` is True. + + """ + return patch_ast(ast.parse(source), source, sorted_children) + + +def patch_ast(node, source, sorted_children=False): + """Patches the given node + + After calling, each node in `node` will have a new field named + `region` that is a tuple containing the start and end offsets + of the code that generated it. + + If `sorted_children` is true, a `sorted_children` field will + be created for each node, too. It is a list containing child + nodes as well as whitespaces and comments that occur between + them. + + """ + if hasattr(node, 'region'): + return node + walker = _PatchingASTWalker(source, children=sorted_children) + ast.call_for_nodes(node, walker) + return node + + +def node_region(patched_ast_node): + """Get the region of a patched ast node""" + return patched_ast_node.region + + +def write_ast(patched_ast_node): + """Extract source form a patched AST node with `sorted_children` field + + If the node is patched with sorted_children turned off you can use + `node_region` function for obtaining code using module source code. + """ + result = [] + for child in patched_ast_node.sorted_children: + if isinstance(child, ast.AST): + result.append(write_ast(child)) + else: + result.append(child) + return ''.join(result) + + +class MismatchedTokenError(exceptions.RopeError): + pass + + +class _PatchingASTWalker(object): + + def __init__(self, source, children=False): + self.source = _Source(source) + self.children = children + self.lines = codeanalyze.SourceLinesAdapter(source) + self.children_stack = [] + + Number = object() + String = object() + semicolon_or_as_in_except = object() + + def __call__(self, node): + method = getattr(self, '_' + node.__class__.__name__, None) + if method is not None: + return method(node) + # ???: Unknown node; what should we do here? + warnings.warn('Unknown node type <%s>; please report!' + % node.__class__.__name__, RuntimeWarning) + node.region = (self.source.offset, self.source.offset) + if self.children: + node.sorted_children = ast.get_children(node) + + def _handle(self, node, base_children, eat_parens=False, eat_spaces=False): + if hasattr(node, 'region'): + # ???: The same node was seen twice; what should we do? + warnings.warn( + 'Node <%s> has been already patched; please report!' % + node.__class__.__name__, RuntimeWarning) + return + base_children = collections.deque(base_children) + self.children_stack.append(base_children) + children = collections.deque() + formats = [] + suspected_start = self.source.offset + start = suspected_start + first_token = True + while base_children: + child = base_children.popleft() + if child is None: + continue + offset = self.source.offset + if isinstance(child, ast.AST): + ast.call_for_nodes(child, self) + token_start = child.region[0] + else: + if child is self.String: + region = self.source.consume_string( + end=self._find_next_statement_start()) + elif child is self.Number: + region = self.source.consume_number() + elif child == '!=': + # INFO: This has been added to handle deprecated ``<>`` + region = self.source.consume_not_equal() + elif child == self.semicolon_or_as_in_except: + # INFO: This has been added to handle deprecated + # semicolon in except + region = self.source.consume_except_as_or_semicolon() + else: + region = self.source.consume(child) + child = self.source[region[0]:region[1]] + token_start = region[0] + if not first_token: + formats.append(self.source[offset:token_start]) + if self.children: + children.append(self.source[offset:token_start]) + else: + first_token = False + start = token_start + if self.children: + children.append(child) + start = self._handle_parens(children, start, formats) + if eat_parens: + start = self._eat_surrounding_parens( + children, suspected_start, start) + if eat_spaces: + if self.children: + children.appendleft(self.source[0:start]) + end_spaces = self.source[self.source.offset:] + self.source.consume(end_spaces) + if self.children: + children.append(end_spaces) + start = 0 + if self.children: + node.sorted_children = children + node.region = (start, self.source.offset) + self.children_stack.pop() + + def _handle_parens(self, children, start, formats): + """Changes `children` and returns new start""" + opens, closes = self._count_needed_parens(formats) + old_end = self.source.offset + new_end = None + for i in range(closes): + new_end = self.source.consume(')')[1] + if new_end is not None: + if self.children: + children.append(self.source[old_end:new_end]) + new_start = start + for i in range(opens): + new_start = self.source.rfind_token('(', 0, new_start) + if new_start != start: + if self.children: + children.appendleft(self.source[new_start:start]) + start = new_start + return start + + def _eat_surrounding_parens(self, children, suspected_start, start): + index = self.source.rfind_token('(', suspected_start, start) + if index is not None: + old_start = start + old_offset = self.source.offset + start = index + if self.children: + children.appendleft(self.source[start + 1:old_start]) + children.appendleft('(') + token_start, token_end = self.source.consume(')') + if self.children: + children.append(self.source[old_offset:token_start]) + children.append(')') + return start + + def _count_needed_parens(self, children): + start = 0 + opens = 0 + for child in children: + if not isinstance(child, basestring): + continue + if child == '' or child[0] in '\'"': + continue + index = 0 + while index < len(child): + if child[index] == ')': + if opens > 0: + opens -= 1 + else: + start += 1 + if child[index] == '(': + opens += 1 + if child[index] == '#': + try: + index = child.index('\n', index) + except ValueError: + break + index += 1 + return start, opens + + def _find_next_statement_start(self): + for children in reversed(self.children_stack): + for child in children: + if isinstance(child, ast.stmt): + return child.col_offset \ + + self.lines.get_line_start(child.lineno) + return len(self.source.source) + + _operators = {'And': 'and', 'Or': 'or', 'Add': '+', 'Sub': '-', + 'Mult': '*', 'Div': '/', 'Mod': '%', 'Pow': '**', + 'LShift': '<<', 'RShift': '>>', 'BitOr': '|', 'BitAnd': '&', + 'BitXor': '^', 'FloorDiv': '//', 'Invert': '~', + 'Not': 'not', 'UAdd': '+', 'USub': '-', 'Eq': '==', + 'NotEq': '!=', 'Lt': '<', 'LtE': '<=', 'Gt': '>', + 'GtE': '>=', 'Is': 'is', 'IsNot': 'is not', 'In': 'in', + 'NotIn': 'not in'} + + def _get_op(self, node): + return self._operators[node.__class__.__name__].split(' ') + + def _Attribute(self, node): + self._handle(node, [node.value, '.', node.attr]) + + def _Assert(self, node): + children = ['assert', node.test] + if node.msg: + children.append(',') + children.append(node.msg) + self._handle(node, children) + + def _Assign(self, node): + children = self._child_nodes(node.targets, '=') + children.append('=') + children.append(node.value) + self._handle(node, children) + + def _AugAssign(self, node): + children = [node.target] + children.extend(self._get_op(node.op)) + children.extend(['=', node.value]) + self._handle(node, children) + + def _Repr(self, node): + self._handle(node, ['`', node.value, '`']) + + def _BinOp(self, node): + children = [node.left] + self._get_op(node.op) + [node.right] + self._handle(node, children) + + def _BoolOp(self, node): + self._handle(node, self._child_nodes(node.values, + self._get_op(node.op)[0])) + + def _Break(self, node): + self._handle(node, ['break']) + + def _Call(self, node): + def _arg_sort_key(node): + if isinstance(node, ast.keyword): + return (node.value.lineno, node.value.col_offset) + return (node.lineno, node.col_offset) + + children = [node.func, '('] + unstarred_args = [] + starred_and_keywords = list(node.keywords) + for i, arg in enumerate(node.args): + if hasattr(ast, 'Starred') and isinstance(arg, ast.Starred): + starred_and_keywords.append(arg) + else: + unstarred_args.append(arg) + if getattr(node, 'starargs', None): + starred_and_keywords.append(node.starargs) + starred_and_keywords.sort(key=_arg_sort_key) + children.extend(self._child_nodes(unstarred_args, ',')) + + # positional args come before keywords, *args comes after all + # positional args, and **kwargs comes last + if starred_and_keywords: + if len(children) > 2: + children.append(',') + for i, arg in enumerate(starred_and_keywords): + if arg == getattr(node, 'starargs', None): + children.append('*') + children.append(arg) + if i + 1 < len(starred_and_keywords): + children.append(',') + + if getattr(node, 'kwargs', None): + if len(children) > 2: + children.append(',') + children.extend(['**', node.kwargs]) + children.append(')') + self._handle(node, children) + + def _ClassDef(self, node): + children = [] + if getattr(node, 'decorator_list', None): + for decorator in node.decorator_list: + children.append('@') + children.append(decorator) + children.extend(['class', node.name]) + if node.bases: + children.append('(') + children.extend(self._child_nodes(node.bases, ',')) + children.append(')') + children.append(':') + children.extend(node.body) + self._handle(node, children) + + def _Compare(self, node): + children = [] + children.append(node.left) + for op, expr in zip(node.ops, node.comparators): + children.extend(self._get_op(op)) + children.append(expr) + self._handle(node, children) + + def _Delete(self, node): + self._handle(node, ['del'] + self._child_nodes(node.targets, ',')) + + def _Num(self, node): + self._handle(node, [self.Number]) + + def _Str(self, node): + self._handle(node, [self.String]) + + def _Continue(self, node): + self._handle(node, ['continue']) + + def _Dict(self, node): + children = [] + children.append('{') + if node.keys: + for index, (key, value) in enumerate(zip(node.keys, node.values)): + children.extend([key, ':', value]) + if index < len(node.keys) - 1: + children.append(',') + children.append('}') + self._handle(node, children) + + def _Ellipsis(self, node): + self._handle(node, ['...']) + + def _Expr(self, node): + self._handle(node, [node.value]) + + def _Exec(self, node): + children = [] + children.extend(['exec', node.body]) + if node.globals: + children.extend(['in', node.globals]) + if node.locals: + children.extend([',', node.locals]) + self._handle(node, children) + + def _ExtSlice(self, node): + children = [] + for index, dim in enumerate(node.dims): + if index > 0: + children.append(',') + children.append(dim) + self._handle(node, children) + + def _For(self, node): + children = ['for', node.target, 'in', node.iter, ':'] + children.extend(node.body) + if node.orelse: + children.extend(['else', ':']) + children.extend(node.orelse) + self._handle(node, children) + + def _ImportFrom(self, node): + children = ['from'] + if node.level: + children.append('.' * node.level) + # see comment at rope.base.ast.walk + children.extend([node.module or '', + 'import']) + children.extend(self._child_nodes(node.names, ',')) + self._handle(node, children) + + def _alias(self, node): + children = [node.name] + if node.asname: + children.extend(['as', node.asname]) + self._handle(node, children) + + def _FunctionDef(self, node): + children = [] + try: + decorators = getattr(node, 'decorator_list') + except AttributeError: + decorators = getattr(node, 'decorators', None) + if decorators: + for decorator in decorators: + children.append('@') + children.append(decorator) + children.extend(['def', node.name, '(', node.args]) + children.extend([')', ':']) + children.extend(node.body) + self._handle(node, children) + + def _arguments(self, node): + children = [] + args = list(node.args) + defaults = [None] * (len(args) - len(node.defaults)) + \ + list(node.defaults) + for index, (arg, default) in enumerate(zip(args, defaults)): + if index > 0: + children.append(',') + self._add_args_to_children(children, arg, default) + if node.vararg is not None: + if args: + children.append(',') + children.extend(['*', pycompat.get_ast_arg_arg(node.vararg)]) + if node.kwarg is not None: + if args or node.vararg is not None: + children.append(',') + children.extend(['**', pycompat.get_ast_arg_arg(node.kwarg)]) + self._handle(node, children) + + def _add_args_to_children(self, children, arg, default): + if isinstance(arg, (list, tuple)): + self._add_tuple_parameter(children, arg) + else: + children.append(arg) + if default is not None: + children.append('=') + children.append(default) + + def _add_tuple_parameter(self, children, arg): + children.append('(') + for index, token in enumerate(arg): + if index > 0: + children.append(',') + if isinstance(token, (list, tuple)): + self._add_tuple_parameter(children, token) + else: + children.append(token) + children.append(')') + + def _GeneratorExp(self, node): + children = [node.elt] + children.extend(node.generators) + self._handle(node, children, eat_parens=True) + + def _comprehension(self, node): + children = ['for', node.target, 'in', node.iter] + if node.ifs: + for if_ in node.ifs: + children.append('if') + children.append(if_) + self._handle(node, children) + + def _Global(self, node): + children = self._child_nodes(node.names, ',') + children.insert(0, 'global') + self._handle(node, children) + + def _If(self, node): + if self._is_elif(node): + children = ['elif'] + else: + children = ['if'] + children.extend([node.test, ':']) + children.extend(node.body) + if node.orelse: + if len(node.orelse) == 1 and self._is_elif(node.orelse[0]): + pass + else: + children.extend(['else', ':']) + children.extend(node.orelse) + self._handle(node, children) + + def _is_elif(self, node): + if not isinstance(node, ast.If): + return False + offset = self.lines.get_line_start(node.lineno) + node.col_offset + word = self.source[offset:offset + 4] + # XXX: This is a bug; the offset does not point to the first + alt_word = self.source[offset - 5:offset - 1] + return 'elif' in (word, alt_word) + + def _IfExp(self, node): + return self._handle(node, [node.body, 'if', node.test, + 'else', node.orelse]) + + def _Import(self, node): + children = ['import'] + children.extend(self._child_nodes(node.names, ',')) + self._handle(node, children) + + def _keyword(self, node): + children = [] + if node.arg is None: + children.append(node.value) + else: + children.extend([node.arg, '=', node.value]) + self._handle(node, children) + + def _Lambda(self, node): + self._handle(node, ['lambda', node.args, ':', node.body]) + + def _List(self, node): + self._handle(node, ['['] + self._child_nodes(node.elts, ',') + [']']) + + def _ListComp(self, node): + children = ['[', node.elt] + children.extend(node.generators) + children.append(']') + self._handle(node, children) + + def _Set(self, node): + if node.elts: + self._handle(node, + ['{'] + self._child_nodes(node.elts, ',') + ['}']) + return + # Python doesn't have empty set literals + warnings.warn('Tried to handle empty <Set> literal; please report!', + RuntimeWarning) + self._handle(node, ['set(', ')']) + + def _SetComp(self, node): + children = ['{', node.elt] + children.extend(node.generators) + children.append('}') + self._handle(node, children) + + def _DictComp(self, node): + children = ['{'] + children.extend([node.key, ':', node.value]) + children.extend(node.generators) + children.append('}') + self._handle(node, children) + + def _Module(self, node): + self._handle(node, list(node.body), eat_spaces=True) + + def _Name(self, node): + self._handle(node, [node.id]) + + def _NameConstant(self, node): + self._handle(node, [str(node.value)]) + + def _arg(self, node): + self._handle(node, [node.arg]) + + def _Pass(self, node): + self._handle(node, ['pass']) + + def _Print(self, node): + children = ['print'] + if node.dest: + children.extend(['>>', node.dest]) + if node.values: + children.append(',') + children.extend(self._child_nodes(node.values, ',')) + if not node.nl: + children.append(',') + self._handle(node, children) + + def _Raise(self, node): + + def get_python3_raise_children(node): + children = ['raise'] + if node.exc: + children.append(node.exc) + if node.cause: + children.append(node.cause) + return children + + def get_python2_raise_children(node): + children = ['raise'] + if node.type: + children.append(node.type) + if node.inst: + children.append(',') + children.append(node.inst) + if node.tback: + children.append(',') + children.append(node.tback) + return children + if pycompat.PY2: + children = get_python2_raise_children(node) + else: + children = get_python3_raise_children(node) + self._handle(node, children) + + def _Return(self, node): + children = ['return'] + if node.value: + children.append(node.value) + self._handle(node, children) + + def _Sliceobj(self, node): + children = [] + for index, slice in enumerate(node.nodes): + if index > 0: + children.append(':') + if slice: + children.append(slice) + self._handle(node, children) + + def _Index(self, node): + self._handle(node, [node.value]) + + def _Subscript(self, node): + self._handle(node, [node.value, '[', node.slice, ']']) + + def _Slice(self, node): + children = [] + if node.lower: + children.append(node.lower) + children.append(':') + if node.upper: + children.append(node.upper) + if node.step: + children.append(':') + children.append(node.step) + self._handle(node, children) + + def _TryFinally(self, node): + # @todo fixme + is_there_except_handler = False + not_empty_body = True + if len(node.finalbody) == 1: + if pycompat.PY2: + is_there_except_handler = isinstance(node.body[0], ast.TryExcept) + not_empty_body = not bool(len(node.body)) + elif pycompat.PY3: + try: + is_there_except_handler = isinstance(node.handlers[0], ast.ExceptHandler) + not_empty_body = True + except IndexError: + pass + children = [] + if not_empty_body or not is_there_except_handler: + children.extend(['try', ':']) + children.extend(node.body) + if pycompat.PY3: + children.extend(node.handlers) + children.extend(['finally', ':']) + children.extend(node.finalbody) + self._handle(node, children) + + def _TryExcept(self, node): + children = ['try', ':'] + children.extend(node.body) + children.extend(node.handlers) + if node.orelse: + children.extend(['else', ':']) + children.extend(node.orelse) + self._handle(node, children) + + def _Try(self, node): + if len(node.finalbody): + self._TryFinally(node) + else: + self._TryExcept(node) + + def _ExceptHandler(self, node): + self._excepthandler(node) + + def _excepthandler(self, node): + # self._handle(node, [self.semicolon_or_as_in_except]) + children = ['except'] + if node.type: + children.append(node.type) + if node.name: + children.append(self.semicolon_or_as_in_except) + children.append(node.name) + children.append(':') + children.extend(node.body) + + self._handle(node, children) + + def _Tuple(self, node): + if node.elts: + self._handle(node, self._child_nodes(node.elts, ','), + eat_parens=True) + else: + self._handle(node, ['(', ')']) + + def _UnaryOp(self, node): + children = self._get_op(node.op) + children.append(node.operand) + self._handle(node, children) + + def _Yield(self, node): + children = ['yield'] + if node.value: + children.append(node.value) + self._handle(node, children) + + def _While(self, node): + children = ['while', node.test, ':'] + children.extend(node.body) + if node.orelse: + children.extend(['else', ':']) + children.extend(node.orelse) + self._handle(node, children) + + def _With(self, node): + children = [] + for item in pycompat.get_ast_with_items(node): + children.extend(['with', item.context_expr]) + if item.optional_vars: + children.extend(['as', item.optional_vars]) + children.append(':') + children.extend(node.body) + self._handle(node, children) + + def _child_nodes(self, nodes, separator): + children = [] + for index, child in enumerate(nodes): + children.append(child) + if index < len(nodes) - 1: + children.append(separator) + return children + + def _Starred(self, node): + self._handle(node, [node.value]) + +class _Source(object): + + def __init__(self, source): + self.source = source + self.offset = 0 + + def consume(self, token): + try: + while True: + new_offset = self.source.index(token, self.offset) + if self._good_token(token, new_offset): + break + else: + self._skip_comment() + except (ValueError, TypeError): + raise MismatchedTokenError( + 'Token <%s> at %s cannot be matched' % + (token, self._get_location())) + self.offset = new_offset + len(token) + return (new_offset, self.offset) + + def consume_string(self, end=None): + if _Source._string_pattern is None: + original = codeanalyze.get_string_pattern() + pattern = r'(%s)((\s|\\\n|#[^\n]*\n)*(%s))*' % \ + (original, original) + _Source._string_pattern = re.compile(pattern) + repattern = _Source._string_pattern + return self._consume_pattern(repattern, end) + + def consume_number(self): + if _Source._number_pattern is None: + _Source._number_pattern = re.compile( + self._get_number_pattern()) + repattern = _Source._number_pattern + return self._consume_pattern(repattern) + + def consume_not_equal(self): + if _Source._not_equals_pattern is None: + _Source._not_equals_pattern = re.compile(r'<>|!=') + repattern = _Source._not_equals_pattern + return self._consume_pattern(repattern) + + def consume_except_as_or_semicolon(self): + repattern = re.compile(r'as|,') + return self._consume_pattern(repattern) + + def _good_token(self, token, offset, start=None): + """Checks whether consumed token is in comments""" + if start is None: + start = self.offset + try: + comment_index = self.source.rindex('#', start, offset) + except ValueError: + return True + try: + new_line_index = self.source.rindex('\n', start, offset) + except ValueError: + return False + return comment_index < new_line_index + + def _skip_comment(self): + self.offset = self.source.index('\n', self.offset + 1) + + def _get_location(self): + lines = self.source[:self.offset].split('\n') + return (len(lines), len(lines[-1])) + + def _consume_pattern(self, repattern, end=None): + while True: + if end is None: + end = len(self.source) + match = repattern.search(self.source, self.offset, end) + if self._good_token(match.group(), match.start()): + break + else: + self._skip_comment() + self.offset = match.end() + return match.start(), match.end() + + def till_token(self, token): + new_offset = self.source.index(token, self.offset) + return self[self.offset:new_offset] + + def rfind_token(self, token, start, end): + index = start + while True: + try: + index = self.source.rindex(token, start, end) + if self._good_token(token, index, start=start): + return index + else: + end = index + except ValueError: + return None + + def from_offset(self, offset): + return self[offset:self.offset] + + def find_backwards(self, pattern, offset): + return self.source.rindex(pattern, 0, offset) + + def __getitem__(self, index): + return self.source[index] + + def __getslice__(self, i, j): + return self.source[i:j] + + def _get_number_pattern(self): + # HACK: It is merely an approaximation and does the job + integer = r'\-?(0x[\da-fA-F]+|\d+)[lL]?' + return r'(%s(\.\d*)?|(\.\d+))([eE][-+]?\d+)?[jJ]?' % integer + + _string_pattern = None + _number_pattern = None + _not_equals_pattern = None diff --git a/venv/Lib/site-packages/rope/refactor/rename.py b/venv/Lib/site-packages/rope/refactor/rename.py new file mode 100644 index 0000000000000000000000000000000000000000..3f1f5b7e6d61271124767e29a8be067c96e5199a --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/rename.py @@ -0,0 +1,220 @@ +import warnings + +from rope.base import (exceptions, pyobjects, pynames, taskhandle, + evaluate, worder, codeanalyze, libutils) +from rope.base.change import ChangeSet, ChangeContents, MoveResource +from rope.refactor import occurrences + + +class Rename(object): + """A class for performing rename refactoring + + It can rename everything: classes, functions, modules, packages, + methods, variables and keyword arguments. + + """ + + def __init__(self, project, resource, offset=None): + """If `offset` is None, the `resource` itself will be renamed""" + self.project = project + self.resource = resource + if offset is not None: + self.old_name = worder.get_name_at(self.resource, offset) + this_pymodule = self.project.get_pymodule(self.resource) + self.old_instance, self.old_pyname = \ + evaluate.eval_location2(this_pymodule, offset) + if self.old_pyname is None: + raise exceptions.RefactoringError( + 'Rename refactoring should be performed' + ' on resolvable python identifiers.') + else: + if not resource.is_folder() and resource.name == '__init__.py': + resource = resource.parent + dummy_pymodule = libutils.get_string_module(self.project, '') + self.old_instance = None + self.old_pyname = pynames.ImportedModule(dummy_pymodule, + resource=resource) + if resource.is_folder(): + self.old_name = resource.name + else: + self.old_name = resource.name[:-3] + + def get_old_name(self): + return self.old_name + + def get_changes(self, new_name, in_file=None, in_hierarchy=False, + unsure=None, docs=False, resources=None, + task_handle=taskhandle.NullTaskHandle()): + """Get the changes needed for this refactoring + + Parameters: + + - `in_hierarchy`: when renaming a method this keyword forces + to rename all matching methods in the hierarchy + - `docs`: when `True` rename refactoring will rename + occurrences in comments and strings where the name is + visible. Setting it will make renames faster, too. + - `unsure`: decides what to do about unsure occurrences. + If `None`, they are ignored. Otherwise `unsure` is + called with an instance of `occurrence.Occurrence` as + parameter. If it returns `True`, the occurrence is + considered to be a match. + - `resources` can be a list of `rope.base.resources.File`\s to + apply this refactoring on. If `None`, the restructuring + will be applied to all python files. + - `in_file`: this argument has been deprecated; use + `resources` instead. + + """ + if unsure in (True, False): + warnings.warn( + 'unsure parameter should be a function that returns ' + 'True or False', DeprecationWarning, stacklevel=2) + + def unsure_func(value=unsure): + return value + unsure = unsure_func + if in_file is not None: + warnings.warn( + '`in_file` argument has been deprecated; use `resources` ' + 'instead. ', DeprecationWarning, stacklevel=2) + if in_file: + resources = [self.resource] + if _is_local(self.old_pyname): + resources = [self.resource] + if resources is None: + resources = self.project.get_python_files() + changes = ChangeSet('Renaming <%s> to <%s>' % + (self.old_name, new_name)) + finder = occurrences.create_finder( + self.project, self.old_name, self.old_pyname, unsure=unsure, + docs=docs, instance=self.old_instance, + in_hierarchy=in_hierarchy and self.is_method()) + job_set = task_handle.create_jobset('Collecting Changes', + len(resources)) + for file_ in resources: + job_set.started_job(file_.path) + new_content = rename_in_module(finder, new_name, resource=file_) + if new_content is not None: + changes.add_change(ChangeContents(file_, new_content)) + job_set.finished_job() + if self._is_renaming_a_module(): + resource = self.old_pyname.get_object().get_resource() + if self._is_allowed_to_move(resources, resource): + self._rename_module(resource, new_name, changes) + return changes + + def _is_allowed_to_move(self, resources, resource): + if resource.is_folder(): + try: + return resource.get_child('__init__.py') in resources + except exceptions.ResourceNotFoundError: + return False + else: + return resource in resources + + def _is_renaming_a_module(self): + if isinstance(self.old_pyname.get_object(), pyobjects.AbstractModule): + return True + return False + + def is_method(self): + pyname = self.old_pyname + return isinstance(pyname, pynames.DefinedName) and \ + isinstance(pyname.get_object(), pyobjects.PyFunction) and \ + isinstance(pyname.get_object().parent, pyobjects.PyClass) + + def _rename_module(self, resource, new_name, changes): + if not resource.is_folder(): + new_name = new_name + '.py' + parent_path = resource.parent.path + if parent_path == '': + new_location = new_name + else: + new_location = parent_path + '/' + new_name + changes.add_change(MoveResource(resource, new_location)) + + +class ChangeOccurrences(object): + """A class for changing the occurrences of a name in a scope + + This class replaces the occurrences of a name. Note that it only + changes the scope containing the offset passed to the constructor. + What's more it does not have any side-effects. That is for + example changing occurrences of a module does not rename the + module; it merely replaces the occurrences of that module in a + scope with the given expression. This class is useful for + performing many custom refactorings. + + """ + + def __init__(self, project, resource, offset): + self.project = project + self.resource = resource + self.offset = offset + self.old_name = worder.get_name_at(resource, offset) + self.pymodule = project.get_pymodule(self.resource) + self.old_pyname = evaluate.eval_location(self.pymodule, offset) + + def get_old_name(self): + word_finder = worder.Worder(self.resource.read()) + return word_finder.get_primary_at(self.offset) + + def _get_scope_offset(self): + lines = self.pymodule.lines + scope = self.pymodule.get_scope().\ + get_inner_scope_for_line(lines.get_line_number(self.offset)) + start = lines.get_line_start(scope.get_start()) + end = lines.get_line_end(scope.get_end()) + return start, end + + def get_changes(self, new_name, only_calls=False, reads=True, writes=True): + changes = ChangeSet('Changing <%s> occurrences to <%s>' % + (self.old_name, new_name)) + scope_start, scope_end = self._get_scope_offset() + finder = occurrences.create_finder( + self.project, self.old_name, self.old_pyname, + imports=False, only_calls=only_calls) + new_contents = rename_in_module( + finder, new_name, pymodule=self.pymodule, replace_primary=True, + region=(scope_start, scope_end), reads=reads, writes=writes) + if new_contents is not None: + changes.add_change(ChangeContents(self.resource, new_contents)) + return changes + + +def rename_in_module(occurrences_finder, new_name, resource=None, + pymodule=None, replace_primary=False, region=None, + reads=True, writes=True): + """Returns the changed source or `None` if there is no changes""" + if resource is not None: + source_code = resource.read() + else: + source_code = pymodule.source_code + change_collector = codeanalyze.ChangeCollector(source_code) + for occurrence in occurrences_finder.find_occurrences(resource, pymodule): + if replace_primary and occurrence.is_a_fixed_primary(): + continue + if replace_primary: + start, end = occurrence.get_primary_range() + else: + start, end = occurrence.get_word_range() + if (not reads and not occurrence.is_written()) or \ + (not writes and occurrence.is_written()): + continue + if region is None or region[0] <= start < region[1]: + change_collector.add_change(start, end, new_name) + return change_collector.get_changed() + + +def _is_local(pyname): + module, lineno = pyname.get_definition_location() + if lineno is None: + return False + scope = module.get_scope().get_inner_scope_for_line(lineno) + if isinstance(pyname, pynames.DefinedName) and \ + scope.get_kind() in ('Function', 'Class'): + scope = scope.parent + return scope.get_kind() == 'Function' and \ + pyname in scope.get_names().values() and \ + isinstance(pyname, pynames.AssignedName) diff --git a/venv/Lib/site-packages/rope/refactor/restructure.py b/venv/Lib/site-packages/rope/refactor/restructure.py new file mode 100644 index 0000000000000000000000000000000000000000..98a11e3d7741b0311a42a1068003747a1eb01a69 --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/restructure.py @@ -0,0 +1,307 @@ +import warnings + +from rope.base import change, taskhandle, builtins, ast, codeanalyze +from rope.base import libutils +from rope.refactor import patchedast, similarfinder, sourceutils +from rope.refactor.importutils import module_imports + + +class Restructure(object): + """A class to perform python restructurings + + A restructuring transforms pieces of code matching `pattern` to + `goal`. In the `pattern` wildcards can appear. Wildcards match + some piece of code based on their kind and arguments that are + passed to them through `args`. + + `args` is a dictionary of wildcard names to wildcard arguments. + If the argument is a tuple, the first item of the tuple is + considered to be the name of the wildcard to use; otherwise the + "default" wildcard is used. For getting the list arguments a + wildcard supports, see the pydoc of the wildcard. (see + `rope.refactor.wildcard.DefaultWildcard` for the default + wildcard.) + + `wildcards` is the list of wildcard types that can appear in + `pattern`. See `rope.refactor.wildcards`. If a wildcard does not + specify its kind (by using a tuple in args), the wildcard named + "default" is used. So there should be a wildcard with "default" + name in `wildcards`. + + `imports` is the list of imports that changed modules should + import. Note that rope handles duplicate imports and does not add + the import if it already appears. + + Example #1:: + + pattern ${pyobject}.get_attribute(${name}) + goal ${pyobject}[${name}] + args pyobject: instance=rope.base.pyobjects.PyObject + + Example #2:: + + pattern ${name} in ${pyobject}.get_attributes() + goal ${name} in {pyobject} + args pyobject: instance=rope.base.pyobjects.PyObject + + Example #3:: + + pattern ${pycore}.create_module(${project}.root, ${name}) + goal generate.create_module(${project}, ${name}) + + imports + from rope.contrib import generate + + args + project: type=rope.base.project.Project + + Example #4:: + + pattern ${pow}(${param1}, ${param2}) + goal ${param1} ** ${param2} + args pow: name=mod.pow, exact + + Example #5:: + + pattern ${inst}.longtask(${p1}, ${p2}) + goal + ${inst}.subtask1(${p1}) + ${inst}.subtask2(${p2}) + args + inst: type=mod.A,unsure + + """ + + def __init__(self, project, pattern, goal, args=None, + imports=None, wildcards=None): + """Construct a restructuring + + See class pydoc for more info about the arguments. + + """ + self.project = project + self.pattern = pattern + self.goal = goal + self.args = args + if self.args is None: + self.args = {} + self.imports = imports + if self.imports is None: + self.imports = [] + self.wildcards = wildcards + self.template = similarfinder.CodeTemplate(self.goal) + + def get_changes(self, checks=None, imports=None, resources=None, + task_handle=taskhandle.NullTaskHandle()): + """Get the changes needed by this restructuring + + `resources` can be a list of `rope.base.resources.File`\s to + apply the restructuring on. If `None`, the restructuring will + be applied to all python files. + + `checks` argument has been deprecated. Use the `args` argument + of the constructor. The usage of:: + + strchecks = {'obj1.type': 'mod.A', 'obj2': 'mod.B', + 'obj3.object': 'mod.C'} + checks = restructuring.make_checks(strchecks) + + can be replaced with:: + + args = {'obj1': 'type=mod.A', 'obj2': 'name=mod.B', + 'obj3': 'object=mod.C'} + + where obj1, obj2 and obj3 are wildcard names that appear + in restructuring pattern. + + """ + if checks is not None: + warnings.warn( + 'The use of checks parameter is deprecated; ' + 'use the args parameter of the constructor instead.', + DeprecationWarning, stacklevel=2) + for name, value in checks.items(): + self.args[name] = similarfinder._pydefined_to_str(value) + if imports is not None: + warnings.warn( + 'The use of imports parameter is deprecated; ' + 'use imports parameter of the constructor, instead.', + DeprecationWarning, stacklevel=2) + self.imports = imports + changes = change.ChangeSet('Restructuring <%s> to <%s>' % + (self.pattern, self.goal)) + if resources is not None: + files = [resource for resource in resources + if libutils.is_python_file(self.project, resource)] + else: + files = self.project.get_python_files() + job_set = task_handle.create_jobset('Collecting Changes', len(files)) + for resource in files: + job_set.started_job(resource.path) + pymodule = self.project.get_pymodule(resource) + finder = similarfinder.SimilarFinder(pymodule, + wildcards=self.wildcards) + matches = list(finder.get_matches(self.pattern, self.args)) + computer = self._compute_changes(matches, pymodule) + result = computer.get_changed() + if result is not None: + imported_source = self._add_imports(resource, result, + self.imports) + changes.add_change(change.ChangeContents(resource, + imported_source)) + job_set.finished_job() + return changes + + def _compute_changes(self, matches, pymodule): + return _ChangeComputer( + pymodule.source_code, pymodule.get_ast(), + pymodule.lines, self.template, matches) + + def _add_imports(self, resource, source, imports): + if not imports: + return source + import_infos = self._get_import_infos(resource, imports) + pymodule = libutils.get_string_module(self.project, source, resource) + imports = module_imports.ModuleImports(self.project, pymodule) + for import_info in import_infos: + imports.add_import(import_info) + return imports.get_changed_source() + + def _get_import_infos(self, resource, imports): + pymodule = libutils.get_string_module( + self.project, '\n'.join(imports), resource) + imports = module_imports.ModuleImports(self.project, pymodule) + return [imports.import_info + for imports in imports.imports] + + def make_checks(self, string_checks): + """Convert str to str dicts to str to PyObject dicts + + This function is here to ease writing a UI. + + """ + checks = {} + for key, value in string_checks.items(): + is_pyname = not key.endswith('.object') and \ + not key.endswith('.type') + evaluated = self._evaluate(value, is_pyname=is_pyname) + if evaluated is not None: + checks[key] = evaluated + return checks + + def _evaluate(self, code, is_pyname=True): + attributes = code.split('.') + pyname = None + if attributes[0] in ('__builtin__', '__builtins__'): + class _BuiltinsStub(object): + def get_attribute(self, name): + return builtins.builtins[name] + pyobject = _BuiltinsStub() + else: + pyobject = self.project.get_module(attributes[0]) + for attribute in attributes[1:]: + pyname = pyobject[attribute] + if pyname is None: + return None + pyobject = pyname.get_object() + return pyname if is_pyname else pyobject + + +def replace(code, pattern, goal): + """used by other refactorings""" + finder = similarfinder.RawSimilarFinder(code) + matches = list(finder.get_matches(pattern)) + ast = patchedast.get_patched_ast(code) + lines = codeanalyze.SourceLinesAdapter(code) + template = similarfinder.CodeTemplate(goal) + computer = _ChangeComputer(code, ast, lines, template, matches) + result = computer.get_changed() + if result is None: + return code + return result + + +class _ChangeComputer(object): + + def __init__(self, code, ast, lines, goal, matches): + self.source = code + self.goal = goal + self.matches = matches + self.ast = ast + self.lines = lines + self.matched_asts = {} + self._nearest_roots = {} + if self._is_expression(): + for match in self.matches: + self.matched_asts[match.ast] = match + + def get_changed(self): + if self._is_expression(): + result = self._get_node_text(self.ast) + if result == self.source: + return None + return result + else: + collector = codeanalyze.ChangeCollector(self.source) + last_end = -1 + for match in self.matches: + start, end = match.get_region() + if start < last_end: + if not self._is_expression(): + continue + last_end = end + replacement = self._get_matched_text(match) + collector.add_change(start, end, replacement) + return collector.get_changed() + + def _is_expression(self): + return self.matches and isinstance(self.matches[0], + similarfinder.ExpressionMatch) + + def _get_matched_text(self, match): + mapping = {} + for name in self.goal.get_names(): + node = match.get_ast(name) + if node is None: + raise similarfinder.BadNameInCheckError( + 'Unknown name <%s>' % name) + force = self._is_expression() and match.ast == node + mapping[name] = self._get_node_text(node, force) + unindented = self.goal.substitute(mapping) + return self._auto_indent(match.get_region()[0], unindented) + + def _get_node_text(self, node, force=False): + if not force and node in self.matched_asts: + return self._get_matched_text(self.matched_asts[node]) + start, end = patchedast.node_region(node) + main_text = self.source[start:end] + collector = codeanalyze.ChangeCollector(main_text) + for node in self._get_nearest_roots(node): + sub_start, sub_end = patchedast.node_region(node) + collector.add_change(sub_start - start, sub_end - start, + self._get_node_text(node)) + result = collector.get_changed() + if result is None: + return main_text + return result + + def _auto_indent(self, offset, text): + lineno = self.lines.get_line_number(offset) + indents = sourceutils.get_indents(self.lines, lineno) + result = [] + for index, line in enumerate(text.splitlines(True)): + if index != 0 and line.strip(): + result.append(' ' * indents) + result.append(line) + return ''.join(result) + + def _get_nearest_roots(self, node): + if node not in self._nearest_roots: + result = [] + for child in ast.get_child_nodes(node): + if child in self.matched_asts: + result.append(child) + else: + result.extend(self._get_nearest_roots(child)) + self._nearest_roots[node] = result + return self._nearest_roots[node] diff --git a/venv/Lib/site-packages/rope/refactor/similarfinder.py b/venv/Lib/site-packages/rope/refactor/similarfinder.py new file mode 100644 index 0000000000000000000000000000000000000000..425f9ed955c27eab2741cfb496f3257747f5bbaf --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/similarfinder.py @@ -0,0 +1,370 @@ +"""This module can be used for finding similar code""" +import re + +import rope.refactor.wildcards +from rope.base import libutils +from rope.base import codeanalyze, exceptions, ast, builtins +from rope.refactor import (patchedast, wildcards) + +from rope.refactor.patchedast import MismatchedTokenError + + +class BadNameInCheckError(exceptions.RefactoringError): + pass + + +class SimilarFinder(object): + """`SimilarFinder` can be used to find similar pieces of code + + See the notes in the `rope.refactor.restructure` module for more + info. + + """ + + def __init__(self, pymodule, wildcards=None): + """Construct a SimilarFinder""" + self.source = pymodule.source_code + try: + self.raw_finder = RawSimilarFinder( + pymodule.source_code, pymodule.get_ast(), self._does_match) + except MismatchedTokenError: + print("in file %s" % pymodule.resource.path) + raise + self.pymodule = pymodule + if wildcards is None: + self.wildcards = {} + for wildcard in [rope.refactor.wildcards. + DefaultWildcard(pymodule.pycore.project)]: + self.wildcards[wildcard.get_name()] = wildcard + else: + self.wildcards = wildcards + + def get_matches(self, code, args={}, start=0, end=None): + self.args = args + if end is None: + end = len(self.source) + skip_region = None + if 'skip' in args.get('', {}): + resource, region = args['']['skip'] + if resource == self.pymodule.get_resource(): + skip_region = region + return self.raw_finder.get_matches(code, start=start, end=end, + skip=skip_region) + + def get_match_regions(self, *args, **kwds): + for match in self.get_matches(*args, **kwds): + yield match.get_region() + + def _does_match(self, node, name): + arg = self.args.get(name, '') + kind = 'default' + if isinstance(arg, (tuple, list)): + kind = arg[0] + arg = arg[1] + suspect = wildcards.Suspect(self.pymodule, node, name) + return self.wildcards[kind].matches(suspect, arg) + + +class RawSimilarFinder(object): + """A class for finding similar expressions and statements""" + + def __init__(self, source, node=None, does_match=None): + if node is None: + node = ast.parse(source) + if does_match is None: + self.does_match = self._simple_does_match + else: + self.does_match = does_match + self._init_using_ast(node, source) + + def _simple_does_match(self, node, name): + return isinstance(node, (ast.expr, ast.Name)) + + def _init_using_ast(self, node, source): + self.source = source + self._matched_asts = {} + if not hasattr(node, 'region'): + patchedast.patch_ast(node, source) + self.ast = node + + def get_matches(self, code, start=0, end=None, skip=None): + """Search for `code` in source and return a list of `Match`\es + + `code` can contain wildcards. ``${name}`` matches normal + names and ``${?name} can match any expression. You can use + `Match.get_ast()` for getting the node that has matched a + given pattern. + + """ + if end is None: + end = len(self.source) + for match in self._get_matched_asts(code): + match_start, match_end = match.get_region() + if start <= match_start and match_end <= end: + if skip is not None and (skip[0] < match_end and + skip[1] > match_start): + continue + yield match + + def _get_matched_asts(self, code): + if code not in self._matched_asts: + wanted = self._create_pattern(code) + matches = _ASTMatcher(self.ast, wanted, + self.does_match).find_matches() + self._matched_asts[code] = matches + return self._matched_asts[code] + + def _create_pattern(self, expression): + expression = self._replace_wildcards(expression) + node = ast.parse(expression) + # Getting Module.Stmt.nodes + nodes = node.body + if len(nodes) == 1 and isinstance(nodes[0], ast.Expr): + # Getting Discard.expr + wanted = nodes[0].value + else: + wanted = nodes + return wanted + + def _replace_wildcards(self, expression): + ropevar = _RopeVariable() + template = CodeTemplate(expression) + mapping = {} + for name in template.get_names(): + mapping[name] = ropevar.get_var(name) + return template.substitute(mapping) + + +class _ASTMatcher(object): + + def __init__(self, body, pattern, does_match): + """Searches the given pattern in the body AST. + + body is an AST node and pattern can be either an AST node or + a list of ASTs nodes + """ + self.body = body + self.pattern = pattern + self.matches = None + self.ropevar = _RopeVariable() + self.matches_callback = does_match + + def find_matches(self): + if self.matches is None: + self.matches = [] + ast.call_for_nodes(self.body, self._check_node, recursive=True) + return self.matches + + def _check_node(self, node): + if isinstance(self.pattern, list): + self._check_statements(node) + else: + self._check_expression(node) + + def _check_expression(self, node): + mapping = {} + if self._match_nodes(self.pattern, node, mapping): + self.matches.append(ExpressionMatch(node, mapping)) + + def _check_statements(self, node): + for child in ast.get_children(node): + if isinstance(child, (list, tuple)): + self.__check_stmt_list(child) + + def __check_stmt_list(self, nodes): + for index in range(len(nodes)): + if len(nodes) - index >= len(self.pattern): + current_stmts = nodes[index:index + len(self.pattern)] + mapping = {} + if self._match_stmts(current_stmts, mapping): + self.matches.append(StatementMatch(current_stmts, mapping)) + + def _match_nodes(self, expected, node, mapping): + if isinstance(expected, ast.Name): + if self.ropevar.is_var(expected.id): + return self._match_wildcard(expected, node, mapping) + if not isinstance(expected, ast.AST): + return expected == node + if expected.__class__ != node.__class__: + return False + + children1 = self._get_children(expected) + children2 = self._get_children(node) + if len(children1) != len(children2): + return False + for child1, child2 in zip(children1, children2): + if isinstance(child1, ast.AST): + if not self._match_nodes(child1, child2, mapping): + return False + elif isinstance(child1, (list, tuple)): + if not isinstance(child2, (list, tuple)) or \ + len(child1) != len(child2): + return False + for c1, c2 in zip(child1, child2): + if not self._match_nodes(c1, c2, mapping): + return False + else: + if child1 != child2: + return False + return True + + def _get_children(self, node): + """Return not `ast.expr_context` children of `node`""" + children = ast.get_children(node) + return [child for child in children + if not isinstance(child, ast.expr_context)] + + def _match_stmts(self, current_stmts, mapping): + if len(current_stmts) != len(self.pattern): + return False + for stmt, expected in zip(current_stmts, self.pattern): + if not self._match_nodes(expected, stmt, mapping): + return False + return True + + def _match_wildcard(self, node1, node2, mapping): + name = self.ropevar.get_base(node1.id) + if name not in mapping: + if self.matches_callback(node2, name): + mapping[name] = node2 + return True + return False + else: + return self._match_nodes(mapping[name], node2, {}) + + +class Match(object): + + def __init__(self, mapping): + self.mapping = mapping + + def get_region(self): + """Returns match region""" + + def get_ast(self, name): + """Return the ast node that has matched rope variables""" + return self.mapping.get(name, None) + + +class ExpressionMatch(Match): + + def __init__(self, ast, mapping): + super(ExpressionMatch, self).__init__(mapping) + self.ast = ast + + def get_region(self): + return self.ast.region + + +class StatementMatch(Match): + + def __init__(self, ast_list, mapping): + super(StatementMatch, self).__init__(mapping) + self.ast_list = ast_list + + def get_region(self): + return self.ast_list[0].region[0], self.ast_list[-1].region[1] + + +class CodeTemplate(object): + + def __init__(self, template): + self.template = template + self._find_names() + + def _find_names(self): + self.names = {} + for match in CodeTemplate._get_pattern().finditer(self.template): + if 'name' in match.groupdict() and \ + match.group('name') is not None: + start, end = match.span('name') + name = self.template[start + 2:end - 1] + if name not in self.names: + self.names[name] = [] + self.names[name].append((start, end)) + + def get_names(self): + return self.names.keys() + + def substitute(self, mapping): + collector = codeanalyze.ChangeCollector(self.template) + for name, occurrences in self.names.items(): + for region in occurrences: + collector.add_change(region[0], region[1], mapping[name]) + result = collector.get_changed() + if result is None: + return self.template + return result + + _match_pattern = None + + @classmethod + def _get_pattern(cls): + if cls._match_pattern is None: + pattern = codeanalyze.get_comment_pattern() + '|' + \ + codeanalyze.get_string_pattern() + '|' + \ + r'(?P<name>\$\{[^\s\$\}]*\})' + cls._match_pattern = re.compile(pattern) + return cls._match_pattern + + +class _RopeVariable(object): + """Transform and identify rope inserted wildcards""" + + _normal_prefix = '__rope__variable_normal_' + _any_prefix = '__rope__variable_any_' + + def get_var(self, name): + if name.startswith('?'): + return self._get_any(name) + else: + return self._get_normal(name) + + def is_var(self, name): + return self._is_normal(name) or self._is_var(name) + + def get_base(self, name): + if self._is_normal(name): + return name[len(self._normal_prefix):] + if self._is_var(name): + return '?' + name[len(self._any_prefix):] + + def _get_normal(self, name): + return self._normal_prefix + name + + def _get_any(self, name): + return self._any_prefix + name[1:] + + def _is_normal(self, name): + return name.startswith(self._normal_prefix) + + def _is_var(self, name): + return name.startswith(self._any_prefix) + + +def make_pattern(code, variables): + variables = set(variables) + collector = codeanalyze.ChangeCollector(code) + + def does_match(node, name): + return isinstance(node, ast.Name) and node.id == name + finder = RawSimilarFinder(code, does_match=does_match) + for variable in variables: + for match in finder.get_matches('${%s}' % variable): + start, end = match.get_region() + collector.add_change(start, end, '${%s}' % variable) + result = collector.get_changed() + return result if result is not None else code + + +def _pydefined_to_str(pydefined): + address = [] + if isinstance(pydefined, + (builtins.BuiltinClass, builtins.BuiltinFunction)): + return '__builtins__.' + pydefined.get_name() + else: + while pydefined.parent is not None: + address.insert(0, pydefined.get_name()) + pydefined = pydefined.parent + module_name = libutils.modname(pydefined.resource) + return '.'.join(module_name.split('.') + address) diff --git a/venv/Lib/site-packages/rope/refactor/sourceutils.py b/venv/Lib/site-packages/rope/refactor/sourceutils.py new file mode 100644 index 0000000000000000000000000000000000000000..9b842906636f5348579f796b40c6dd4a24e3d6d9 --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/sourceutils.py @@ -0,0 +1,91 @@ +from rope.base import codeanalyze + + +def get_indents(lines, lineno): + return codeanalyze.count_line_indents(lines.get_line(lineno)) + + +def find_minimum_indents(source_code): + result = 80 + lines = source_code.split('\n') + for line in lines: + if line.strip() == '': + continue + result = min(result, codeanalyze.count_line_indents(line)) + return result + + +def indent_lines(source_code, amount): + if amount == 0: + return source_code + lines = source_code.splitlines(True) + result = [] + for l in lines: + if l.strip() == '': + result.append('\n') + continue + if amount < 0: + indents = codeanalyze.count_line_indents(l) + result.append(max(0, indents + amount) * ' ' + l.lstrip()) + else: + result.append(' ' * amount + l) + return ''.join(result) + + +def fix_indentation(code, new_indents): + """Change the indentation of `code` to `new_indents`""" + min_indents = find_minimum_indents(code) + return indent_lines(code, new_indents - min_indents) + + +def add_methods(pymodule, class_scope, methods_sources): + source_code = pymodule.source_code + lines = pymodule.lines + insertion_line = class_scope.get_end() + if class_scope.get_scopes(): + insertion_line = class_scope.get_scopes()[-1].get_end() + insertion_offset = lines.get_line_end(insertion_line) + methods = '\n\n' + '\n\n'.join(methods_sources) + indented_methods = fix_indentation( + methods, get_indents(lines, class_scope.get_start()) + + get_indent(pymodule.pycore.project)) + result = [] + result.append(source_code[:insertion_offset]) + result.append(indented_methods) + result.append(source_code[insertion_offset:]) + return ''.join(result) + + +def get_body(pyfunction): + """Return unindented function body""" + # FIXME scope = pyfunction.get_scope() + pymodule = pyfunction.get_module() + start, end = get_body_region(pyfunction) + return fix_indentation(pymodule.source_code[start:end], 0) + + +def get_body_region(defined): + """Return the start and end offsets of function body""" + scope = defined.get_scope() + pymodule = defined.get_module() + lines = pymodule.lines + node = defined.get_ast() + start_line = node.lineno + if defined.get_doc() is None: + start_line = node.body[0].lineno + elif len(node.body) > 1: + start_line = node.body[1].lineno + start = lines.get_line_start(start_line) + scope_start = pymodule.logical_lines.logical_line_in(scope.start) + if scope_start[1] >= start_line: + # a one-liner! + # XXX: what if colon appears in a string + start = pymodule.source_code.index(':', start) + 1 + while pymodule.source_code[start].isspace(): + start += 1 + end = min(lines.get_line_end(scope.end) + 1, len(pymodule.source_code)) + return start, end + + +def get_indent(project): + return project.prefs.get('indent_size', 4) diff --git a/venv/Lib/site-packages/rope/refactor/suites.py b/venv/Lib/site-packages/rope/refactor/suites.py new file mode 100644 index 0000000000000000000000000000000000000000..6878508088a7073d6b658eec25f13f41713600f3 --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/suites.py @@ -0,0 +1,158 @@ +from rope.base import ast +from rope.base.utils import pycompat + + +def find_visible(node, lines): + """Return the line which is visible from all `lines`""" + root = ast_suite_tree(node) + return find_visible_for_suite(root, lines) + + +def find_visible_for_suite(root, lines): + if len(lines) == 1: + return lines[0] + line1 = lines[0] + line2 = find_visible_for_suite(root, lines[1:]) + suite1 = root.find_suite(line1) + suite2 = root.find_suite(line2) + + def valid(suite): + return suite is not None and not suite.ignored + if valid(suite1) and not valid(suite2): + return line1 + if not valid(suite1) and valid(suite2): + return line2 + if not valid(suite1) and not valid(suite2): + return None + while suite1 != suite2 and suite1.parent != suite2.parent: + if suite1._get_level() < suite2._get_level(): + line2 = suite2.get_start() + suite2 = suite2.parent + elif suite1._get_level() > suite2._get_level(): + line1 = suite1.get_start() + suite1 = suite1.parent + else: + line1 = suite1.get_start() + line2 = suite2.get_start() + suite1 = suite1.parent + suite2 = suite2.parent + if suite1 == suite2: + return min(line1, line2) + return min(suite1.get_start(), suite2.get_start()) + + +def ast_suite_tree(node): + if hasattr(node, 'lineno'): + lineno = node.lineno + else: + lineno = 1 + return Suite(node.body, lineno) + + +class Suite(object): + + def __init__(self, child_nodes, lineno, parent=None, ignored=False): + self.parent = parent + self.lineno = lineno + self.child_nodes = child_nodes + self._children = None + self.ignored = ignored + + def get_start(self): + if self.parent is None: + if self.child_nodes: + return self.local_start() + else: + return 1 + return self.lineno + + def get_children(self): + if self._children is None: + walker = _SuiteWalker(self) + for child in self.child_nodes: + ast.walk(child, walker) + self._children = walker.suites + return self._children + + def local_start(self): + return self.child_nodes[0].lineno + + def local_end(self): + end = self.child_nodes[-1].lineno + if self.get_children(): + end = max(end, self.get_children()[-1].local_end()) + return end + + def find_suite(self, line): + if line is None: + return None + for child in self.get_children(): + if child.local_start() <= line <= child.local_end(): + return child.find_suite(line) + return self + + def _get_level(self): + if self.parent is None: + return 0 + return self.parent._get_level() + 1 + + +class _SuiteWalker(object): + + def __init__(self, suite): + self.suite = suite + self.suites = [] + + def _If(self, node): + self._add_if_like_node(node) + + def _For(self, node): + self._add_if_like_node(node) + + def _While(self, node): + self._add_if_like_node(node) + + def _With(self, node): + self.suites.append(Suite(node.body, node.lineno, self.suite)) + + def _TryFinally(self, node): + proceed_to_except_handler = False + if len(node.finalbody) == 1: + if pycompat.PY2: + proceed_to_except_handler = isinstance(node.body[0], ast.TryExcept) + elif pycompat.PY3: + try: + proceed_to_except_handler = isinstance(node.handlers[0], ast.ExceptHandler) + except IndexError: + pass + if proceed_to_except_handler: + self._TryExcept(node if pycompat.PY3 else node.body[0]) + else: + self.suites.append(Suite(node.body, node.lineno, self.suite)) + self.suites.append(Suite(node.finalbody, node.lineno, self.suite)) + + def _Try(self, node): + if len(node.finalbody) == 1: + self._TryFinally(node) + else: + self._TryExcept(node) + + def _TryExcept(self, node): + self.suites.append(Suite(node.body, node.lineno, self.suite)) + for handler in node.handlers: + self.suites.append(Suite(handler.body, node.lineno, self.suite)) + if node.orelse: + self.suites.append(Suite(node.orelse, node.lineno, self.suite)) + + def _add_if_like_node(self, node): + self.suites.append(Suite(node.body, node.lineno, self.suite)) + if node.orelse: + self.suites.append(Suite(node.orelse, node.lineno, self.suite)) + + def _FunctionDef(self, node): + self.suites.append(Suite(node.body, node.lineno, + self.suite, ignored=True)) + + def _ClassDef(self, node): + self.suites.append(Suite(node.body, node.lineno, + self.suite, ignored=True)) diff --git a/venv/Lib/site-packages/rope/refactor/topackage.py b/venv/Lib/site-packages/rope/refactor/topackage.py new file mode 100644 index 0000000000000000000000000000000000000000..f36a6d528865f589ff22c80493db19d1971b1cb7 --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/topackage.py @@ -0,0 +1,32 @@ +import rope.refactor.importutils +from rope.base.change import ChangeSet, ChangeContents, MoveResource, \ + CreateFolder + + +class ModuleToPackage(object): + + def __init__(self, project, resource): + self.project = project + self.resource = resource + + def get_changes(self): + changes = ChangeSet('Transform <%s> module to package' % + self.resource.path) + new_content = self._transform_relatives_to_absolute(self.resource) + if new_content is not None: + changes.add_change(ChangeContents(self.resource, new_content)) + parent = self.resource.parent + name = self.resource.name[:-3] + changes.add_change(CreateFolder(parent, name)) + parent_path = parent.path + '/' + if not parent.path: + parent_path = '' + new_path = parent_path + '%s/__init__.py' % name + if self.resource.project == self.project: + changes.add_change(MoveResource(self.resource, new_path)) + return changes + + def _transform_relatives_to_absolute(self, resource): + pymodule = self.project.get_pymodule(resource) + import_tools = rope.refactor.importutils.ImportTools(self.project) + return import_tools.relatives_to_absolutes(pymodule) diff --git a/venv/Lib/site-packages/rope/refactor/usefunction.py b/venv/Lib/site-packages/rope/refactor/usefunction.py new file mode 100644 index 0000000000000000000000000000000000000000..85896a98f7750150fea34e9bd9049e88f2b601be --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/usefunction.py @@ -0,0 +1,174 @@ +from rope.base import (change, taskhandle, evaluate, + exceptions, pyobjects, pynames, ast) +from rope.base import libutils +from rope.refactor import restructure, sourceutils, similarfinder + + +class UseFunction(object): + """Try to use a function wherever possible""" + + def __init__(self, project, resource, offset): + self.project = project + self.offset = offset + this_pymodule = project.get_pymodule(resource) + pyname = evaluate.eval_location(this_pymodule, offset) + if pyname is None: + raise exceptions.RefactoringError('Unresolvable name selected') + self.pyfunction = pyname.get_object() + if not isinstance(self.pyfunction, pyobjects.PyFunction) or \ + not isinstance(self.pyfunction.parent, pyobjects.PyModule): + raise exceptions.RefactoringError( + 'Use function works for global functions, only.') + self.resource = self.pyfunction.get_module().get_resource() + self._check_returns() + + def _check_returns(self): + node = self.pyfunction.get_ast() + if _yield_count(node): + raise exceptions.RefactoringError('Use function should not ' + 'be used on generators.') + returns = _return_count(node) + if returns > 1: + raise exceptions.RefactoringError('usefunction: Function has more ' + 'than one return statement.') + if returns == 1 and not _returns_last(node): + raise exceptions.RefactoringError('usefunction: return should ' + 'be the last statement.') + + def get_changes(self, resources=None, + task_handle=taskhandle.NullTaskHandle()): + if resources is None: + resources = self.project.get_python_files() + changes = change.ChangeSet('Using function <%s>' % + self.pyfunction.get_name()) + if self.resource in resources: + newresources = list(resources) + newresources.remove(self.resource) + for c in self._restructure(newresources, task_handle).changes: + changes.add_change(c) + if self.resource in resources: + for c in self._restructure([self.resource], task_handle, + others=False).changes: + changes.add_change(c) + return changes + + def get_function_name(self): + return self.pyfunction.get_name() + + def _restructure(self, resources, task_handle, others=True): + pattern = self._make_pattern() + goal = self._make_goal(import_=others) + imports = None + if others: + imports = ['import %s' % self._module_name()] + + body_region = sourceutils.get_body_region(self.pyfunction) + args_value = {'skip': (self.resource, body_region)} + args = {'': args_value} + + restructuring = restructure.Restructure( + self.project, pattern, goal, args=args, imports=imports) + return restructuring.get_changes(resources=resources, + task_handle=task_handle) + + def _find_temps(self): + return find_temps(self.project, self._get_body()) + + def _module_name(self): + return libutils.modname(self.resource) + + def _make_pattern(self): + params = self.pyfunction.get_param_names() + body = self._get_body() + body = restructure.replace(body, 'return', 'pass') + wildcards = list(params) + wildcards.extend(self._find_temps()) + if self._does_return(): + if self._is_expression(): + replacement = '${%s}' % self._rope_returned + else: + replacement = '%s = ${%s}' % (self._rope_result, + self._rope_returned) + body = restructure.replace( + body, 'return ${%s}' % self._rope_returned, + replacement) + wildcards.append(self._rope_result) + return similarfinder.make_pattern(body, wildcards) + + def _get_body(self): + return sourceutils.get_body(self.pyfunction) + + def _make_goal(self, import_=False): + params = self.pyfunction.get_param_names() + function_name = self.pyfunction.get_name() + if import_: + function_name = self._module_name() + '.' + function_name + goal = '%s(%s)' % (function_name, + ', ' .join(('${%s}' % p) for p in params)) + if self._does_return() and not self._is_expression(): + goal = '${%s} = %s' % (self._rope_result, goal) + return goal + + def _does_return(self): + body = self._get_body() + removed_return = restructure.replace(body, 'return ${result}', '') + return removed_return != body + + def _is_expression(self): + return len(self.pyfunction.get_ast().body) == 1 + + _rope_result = '_rope__result' + _rope_returned = '_rope__returned' + + +def find_temps(project, code): + code = 'def f():\n' + sourceutils.indent_lines(code, 4) + pymodule = libutils.get_string_module(project, code) + result = [] + function_scope = pymodule.get_scope().get_scopes()[0] + for name, pyname in function_scope.get_names().items(): + if isinstance(pyname, pynames.AssignedName): + result.append(name) + return result + + +def _returns_last(node): + return node.body and isinstance(node.body[-1], ast.Return) + + +def _yield_count(node): + visitor = _ReturnOrYieldFinder() + visitor.start_walking(node) + return visitor.yields + + +def _return_count(node): + visitor = _ReturnOrYieldFinder() + visitor.start_walking(node) + return visitor.returns + + +class _ReturnOrYieldFinder(object): + + def __init__(self): + self.returns = 0 + self.yields = 0 + + def _Return(self, node): + self.returns += 1 + + def _Yield(self, node): + self.yields += 1 + + def _FunctionDef(self, node): + pass + + def _ClassDef(self, node): + pass + + def start_walking(self, node): + nodes = [node] + if isinstance(node, ast.FunctionDef): + nodes = ast.get_child_nodes(node) + for child in nodes: + ast.walk(child, self) diff --git a/venv/Lib/site-packages/rope/refactor/wildcards.py b/venv/Lib/site-packages/rope/refactor/wildcards.py new file mode 100644 index 0000000000000000000000000000000000000000..90040c794e261cac5e91dd1380f6d465931ba4a0 --- /dev/null +++ b/venv/Lib/site-packages/rope/refactor/wildcards.py @@ -0,0 +1,178 @@ +from rope.base import ast, evaluate, builtins, pyobjects +from rope.refactor import patchedast, occurrences + + +class Wildcard(object): + + def get_name(self): + """Return the name of this wildcard""" + + def matches(self, suspect, arg): + """Return `True` if `suspect` matches this wildcard""" + + +class Suspect(object): + + def __init__(self, pymodule, node, name): + self.name = name + self.pymodule = pymodule + self.node = node + + +class DefaultWildcard(object): + """The default restructuring wildcard + + The argument passed to this wildcard is in the + ``key1=value1,key2=value2,...`` format. Possible keys are: + + * name - for checking the reference + * type - for checking the type + * object - for checking the object + * instance - for checking types but similar to builtin isinstance + * exact - matching only occurrences with the same name as the wildcard + * unsure - matching unsure occurrences + + """ + + def __init__(self, project): + self.project = project + + def get_name(self): + return 'default' + + def matches(self, suspect, arg=''): + args = parse_arg(arg) + + if not self._check_exact(args, suspect): + return False + if not self._check_object(args, suspect): + return False + return True + + def _check_object(self, args, suspect): + kind = None + expected = None + unsure = args.get('unsure', False) + for check in ['name', 'object', 'type', 'instance']: + if check in args: + kind = check + expected = args[check] + if expected is not None: + checker = _CheckObject(self.project, expected, + kind, unsure=unsure) + return checker(suspect.pymodule, suspect.node) + return True + + def _check_exact(self, args, suspect): + node = suspect.node + if args.get('exact'): + if not isinstance(node, ast.Name) or not node.id == suspect.name: + return False + else: + if not isinstance(node, ast.expr): + return False + return True + + +def parse_arg(arg): + if isinstance(arg, dict): + return arg + result = {} + tokens = arg.split(',') + for token in tokens: + if '=' in token: + parts = token.split('=', 1) + result[parts[0].strip()] = parts[1].strip() + else: + result[token.strip()] = True + return result + + +class _CheckObject(object): + + def __init__(self, project, expected, kind='object', unsure=False): + self.project = project + self.kind = kind + self.unsure = unsure + self.expected = self._evaluate(expected) + + def __call__(self, pymodule, node): + pyname = self._evaluate_node(pymodule, node) + if pyname is None or self.expected is None: + return self.unsure + if self._unsure_pyname(pyname, unbound=self.kind == 'name'): + return True + if self.kind == 'name': + return self._same_pyname(self.expected, pyname) + else: + pyobject = pyname.get_object() + if self.kind == 'object': + objects = [pyobject] + if self.kind == 'type': + objects = [pyobject.get_type()] + if self.kind == 'instance': + objects = [pyobject] + objects.extend(self._get_super_classes(pyobject)) + objects.extend(self._get_super_classes(pyobject.get_type())) + for pyobject in objects: + if self._same_pyobject(self.expected.get_object(), pyobject): + return True + return False + + def _get_super_classes(self, pyobject): + result = [] + if isinstance(pyobject, pyobjects.AbstractClass): + for superclass in pyobject.get_superclasses(): + result.append(superclass) + result.extend(self._get_super_classes(superclass)) + return result + + def _same_pyobject(self, expected, pyobject): + return expected == pyobject + + def _same_pyname(self, expected, pyname): + return occurrences.same_pyname(expected, pyname) + + def _unsure_pyname(self, pyname, unbound=True): + return self.unsure and occurrences.unsure_pyname(pyname, unbound) + + def _split_name(self, name): + parts = name.split('.') + expression, kind = parts[0], parts[-1] + if len(parts) == 1: + kind = 'name' + return expression, kind + + def _evaluate_node(self, pymodule, node): + scope = pymodule.get_scope().get_inner_scope_for_line(node.lineno) + expression = node + if isinstance(expression, ast.Name) and \ + isinstance(expression.ctx, ast.Store): + start, end = patchedast.node_region(expression) + text = pymodule.source_code[start:end] + return evaluate.eval_str(scope, text) + else: + return evaluate.eval_node(scope, expression) + + def _evaluate(self, code): + attributes = code.split('.') + pyname = None + if attributes[0] in ('__builtin__', '__builtins__'): + class _BuiltinsStub(object): + def get_attribute(self, name): + return builtins.builtins[name] + + def __getitem__(self, name): + return builtins.builtins[name] + + def __contains__(self, name): + return name in builtins.builtins + pyobject = _BuiltinsStub() + else: + pyobject = self.project.get_module(attributes[0]) + for attribute in attributes[1:]: + pyname = pyobject[attribute] + if pyname is None: + return None + pyobject = pyname.get_object() + return pyname diff --git a/venv/Lib/site-packages/xgboost/__pycache__/sklearn.cpython-37.pyc b/venv/Lib/site-packages/xgboost/__pycache__/sklearn.cpython-37.pyc index e3553177da796b7342b9f98ec87ec5f3dda20a09..a399c7371452148484f14598b03d2d630f91a0c8 100644 Binary files a/venv/Lib/site-packages/xgboost/__pycache__/sklearn.cpython-37.pyc and b/venv/Lib/site-packages/xgboost/__pycache__/sklearn.cpython-37.pyc differ diff --git a/xgb_sample_submission.csv b/xgb_sample_submission.csv index dda251dd5d660b0f8d5a158946c3aec42fcf6439..7acb28f0fe964797404daeb72a7611938b240d82 100644 --- a/xgb_sample_submission.csv +++ b/xgb_sample_submission.csv @@ -117,7 +117,7 @@ Id,label 115,3 116,2 117,2 -118,0 +118,3 119,0 120,3 121,2 @@ -206,7 +206,7 @@ Id,label 204,0 205,3 206,2 -207,0 +207,3 208,2 209,0 210,0 @@ -271,7 +271,7 @@ Id,label 269,2 270,0 271,0 -272,0 +272,3 273,0 274,0 275,0 @@ -320,7 +320,7 @@ Id,label 318,0 319,0 320,3 -321,1 +321,3 322,3 323,0 324,3 @@ -338,14 +338,14 @@ Id,label 336,2 337,1 338,1 -339,0 +339,3 340,0 341,0 342,1 343,0 344,0 345,0 -346,0 +346,3 347,0 348,3 349,3 @@ -414,7 +414,7 @@ Id,label 412,3 413,0 414,3 -415,3 +415,0 416,3 417,0 418,3 @@ -481,7 +481,7 @@ Id,label 479,2 480,0 481,0 -482,0 +482,3 483,2 484,3 485,3 @@ -541,7 +541,7 @@ Id,label 539,0 540,1 541,3 -542,0 +542,3 543,3 544,0 545,2 @@ -643,7 +643,7 @@ Id,label 641,2 642,0 643,0 -644,0 +644,3 645,3 646,0 647,2 @@ -696,7 +696,7 @@ Id,label 694,2 695,3 696,0 -697,3 +697,0 698,1 699,0 700,0 @@ -709,7 +709,7 @@ Id,label 707,2 708,2 709,0 -710,0 +710,3 711,1 712,0 713,0 @@ -761,7 +761,7 @@ Id,label 759,1 760,3 761,3 -762,0 +762,3 763,1 764,3 765,3 @@ -804,7 +804,7 @@ Id,label 802,2 803,2 804,3 -805,0 +805,3 806,0 807,2 808,2 @@ -877,7 +877,7 @@ Id,label 875,1 876,3 877,0 -878,0 +878,3 879,1 880,3 881,0 @@ -1039,7 +1039,7 @@ Id,label 1037,0 1038,1 1039,1 -1040,3 +1040,0 1041,2 1042,3 1043,1 @@ -1072,7 +1072,7 @@ Id,label 1070,0 1071,0 1072,1 -1073,0 +1073,3 1074,2 1075,2 1076,0 @@ -1099,8 +1099,8 @@ Id,label 1097,0 1098,1 1099,0 -1100,0 -1101,0 +1100,3 +1101,3 1102,3 1103,1 1104,1 @@ -1110,7 +1110,7 @@ Id,label 1108,2 1109,3 1110,1 -1111,0 +1111,3 1112,0 1113,0 1114,0 @@ -1169,7 +1169,7 @@ Id,label 1167,3 1168,2 1169,3 -1170,0 +1170,3 1171,0 1172,0 1173,1 @@ -1202,7 +1202,7 @@ Id,label 1200,3 1201,0 1202,0 -1203,0 +1203,3 1204,0 1205,2 1206,0 @@ -1224,7 +1224,7 @@ Id,label 1222,0 1223,3 1224,2 -1225,0 +1225,3 1226,2 1227,0 1228,3 @@ -1308,7 +1308,7 @@ Id,label 1306,2 1307,0 1308,0 -1309,0 +1309,3 1310,1 1311,3 1312,3 @@ -1405,7 +1405,7 @@ Id,label 1403,2 1404,0 1405,3 -1406,0 +1406,3 1407,2 1408,0 1409,0 @@ -1559,12 +1559,12 @@ Id,label 1557,2 1558,1 1559,0 -1560,0 +1560,3 1561,2 1562,0 1563,0 1564,2 -1565,1 +1565,3 1566,0 1567,3 1568,0 @@ -1757,7 +1757,7 @@ Id,label 1755,2 1756,0 1757,3 -1758,0 +1758,3 1759,1 1760,0 1761,0 @@ -1818,8 +1818,8 @@ Id,label 1816,3 1817,0 1818,0 -1819,0 -1820,3 +1819,2 +1820,0 1821,0 1822,0 1823,0 @@ -1963,7 +1963,7 @@ Id,label 1961,1 1962,2 1963,2 -1964,0 +1964,3 1965,0 1966,2 1967,2 @@ -1990,13 +1990,13 @@ Id,label 1988,3 1989,0 1990,0 -1991,0 +1991,3 1992,0 1993,2 1994,0 1995,0 1996,3 -1997,2 +1997,0 1998,2 1999,0 2000,0 @@ -2106,7 +2106,7 @@ Id,label 2104,2 2105,0 2106,0 -2107,0 +2107,2 2108,0 2109,2 2110,2 @@ -2231,7 +2231,7 @@ Id,label 2229,0 2230,2 2231,0 -2232,0 +2232,3 2233,3 2234,0 2235,0 @@ -2439,7 +2439,7 @@ Id,label 2437,3 2438,2 2439,0 -2440,0 +2440,3 2441,0 2442,2 2443,3 @@ -2502,7 +2502,7 @@ Id,label 2500,2 2501,3 2502,3 -2503,1 +2503,3 2504,2 2505,0 2506,0 @@ -2516,7 +2516,7 @@ Id,label 2514,1 2515,0 2516,3 -2517,0 +2517,3 2518,1 2519,1 2520,0 @@ -2698,7 +2698,7 @@ Id,label 2696,3 2697,2 2698,0 -2699,0 +2699,3 2700,2 2701,3 2702,0 @@ -2803,7 +2803,7 @@ Id,label 2801,0 2802,0 2803,0 -2804,0 +2804,3 2805,3 2806,0 2807,3 @@ -2836,7 +2836,7 @@ Id,label 2834,2 2835,0 2836,0 -2837,3 +2837,0 2838,2 2839,2 2840,2 @@ -2942,7 +2942,7 @@ Id,label 2940,2 2941,3 2942,0 -2943,0 +2943,3 2944,1 2945,0 2946,0 @@ -3037,7 +3037,7 @@ Id,label 3035,3 3036,3 3037,0 -3038,0 +3038,3 3039,0 3040,3 3041,1 @@ -3055,7 +3055,7 @@ Id,label 3053,1 3054,0 3055,2 -3056,0 +3056,3 3057,2 3058,0 3059,3 @@ -3070,7 +3070,7 @@ Id,label 3068,1 3069,0 3070,0 -3071,0 +3071,3 3072,3 3073,3 3074,3 @@ -3230,8 +3230,8 @@ Id,label 3228,0 3229,2 3230,0 -3231,3 -3232,0 +3231,0 +3232,3 3233,1 3234,3 3235,0 @@ -3243,7 +3243,7 @@ Id,label 3241,2 3242,3 3243,0 -3244,0 +3244,3 3245,1 3246,2 3247,0 @@ -3277,7 +3277,7 @@ Id,label 3275,0 3276,0 3277,0 -3278,0 +3278,3 3279,0 3280,2 3281,1 @@ -3339,7 +3339,7 @@ Id,label 3337,0 3338,1 3339,2 -3340,0 +3340,3 3341,0 3342,0 3343,0 @@ -3360,7 +3360,7 @@ Id,label 3358,0 3359,3 3360,0 -3361,0 +3361,3 3362,0 3363,3 3364,0 @@ -3388,7 +3388,7 @@ Id,label 3386,0 3387,2 3388,1 -3389,0 +3389,3 3390,0 3391,0 3392,3 @@ -3450,7 +3450,7 @@ Id,label 3448,1 3449,3 3450,0 -3451,3 +3451,0 3452,3 3453,0 3454,0 @@ -3555,7 +3555,7 @@ Id,label 3553,3 3554,0 3555,2 -3556,0 +3556,3 3557,2 3558,3 3559,0 @@ -3572,7 +3572,7 @@ Id,label 3570,2 3571,2 3572,0 -3573,0 +3573,3 3574,0 3575,0 3576,0 @@ -3604,7 +3604,7 @@ Id,label 3602,3 3603,2 3604,0 -3605,0 +3605,3 3606,2 3607,0 3608,0 @@ -3623,7 +3623,7 @@ Id,label 3621,1 3622,3 3623,1 -3624,3 +3624,0 3625,3 3626,3 3627,3 @@ -3753,7 +3753,7 @@ Id,label 3751,0 3752,3 3753,2 -3754,0 +3754,3 3755,2 3756,3 3757,0 @@ -3778,7 +3778,7 @@ Id,label 3776,0 3777,0 3778,0 -3779,1 +3779,0 3780,1 3781,0 3782,0 @@ -3808,7 +3808,7 @@ Id,label 3806,2 3807,0 3808,3 -3809,3 +3809,0 3810,2 3811,3 3812,2 @@ -3923,7 +3923,7 @@ Id,label 3921,2 3922,1 3923,1 -3924,0 +3924,3 3925,0 3926,1 3927,2 @@ -3961,7 +3961,7 @@ Id,label 3959,0 3960,0 3961,0 -3962,0 +3962,3 3963,2 3964,2 3965,3 @@ -3976,7 +3976,7 @@ Id,label 3974,3 3975,0 3976,0 -3977,3 +3977,0 3978,3 3979,1 3980,3 @@ -4079,7 +4079,7 @@ Id,label 4077,0 4078,0 4079,2 -4080,0 +4080,3 4081,1 4082,0 4083,0 @@ -4126,7 +4126,7 @@ Id,label 4124,0 4125,2 4126,2 -4127,0 +4127,3 4128,0 4129,3 4130,0 @@ -4160,7 +4160,7 @@ Id,label 4158,1 4159,2 4160,3 -4161,0 +4161,3 4162,2 4163,2 4164,0 @@ -4174,7 +4174,7 @@ Id,label 4172,2 4173,0 4174,0 -4175,0 +4175,3 4176,1 4177,0 4178,2 @@ -4218,7 +4218,7 @@ Id,label 4216,1 4217,0 4218,0 -4219,0 +4219,3 4220,0 4221,3 4222,2 @@ -4244,7 +4244,7 @@ Id,label 4242,0 4243,0 4244,0 -4245,3 +4245,0 4246,0 4247,1 4248,3 @@ -4314,7 +4314,7 @@ Id,label 4312,0 4313,3 4314,2 -4315,0 +4315,3 4316,2 4317,3 4318,0 @@ -4441,7 +4441,7 @@ Id,label 4439,0 4440,0 4441,2 -4442,0 +4442,3 4443,2 4444,3 4445,0 @@ -4473,13 +4473,13 @@ Id,label 4471,0 4472,1 4473,1 -4474,0 +4474,3 4475,1 4476,0 4477,2 4478,3 4479,0 -4480,3 +4480,0 4481,3 4482,1 4483,3 @@ -4496,8 +4496,8 @@ Id,label 4494,1 4495,0 4496,3 -4497,0 -4498,3 +4497,3 +4498,0 4499,0 4500,0 4501,0 @@ -4522,7 +4522,7 @@ Id,label 4520,0 4521,2 4522,2 -4523,0 +4523,3 4524,2 4525,0 4526,3 @@ -4652,7 +4652,7 @@ Id,label 4650,3 4651,0 4652,0 -4653,0 +4653,3 4654,0 4655,0 4656,3 @@ -4682,7 +4682,7 @@ Id,label 4680,2 4681,0 4682,3 -4683,0 +4683,3 4684,0 4685,2 4686,0 @@ -4750,7 +4750,7 @@ Id,label 4748,2 4749,0 4750,2 -4751,3 +4751,0 4752,1 4753,1 4754,0 @@ -4844,7 +4844,7 @@ Id,label 4842,3 4843,0 4844,0 -4845,0 +4845,3 4846,0 4847,0 4848,3 @@ -4909,7 +4909,7 @@ Id,label 4907,0 4908,0 4909,0 -4910,0 +4910,3 4911,1 4912,2 4913,3 @@ -4998,12 +4998,12 @@ Id,label 4996,3 4997,0 4998,2 -4999,0 +4999,3 5000,0 5001,0 5002,0 5003,1 -5004,0 +5004,3 5005,0 5006,0 5007,0 @@ -5110,7 +5110,7 @@ Id,label 5108,3 5109,0 5110,0 -5111,0 +5111,3 5112,2 5113,1 5114,3 @@ -5122,12 +5122,12 @@ Id,label 5120,2 5121,2 5122,1 -5123,0 +5123,3 5124,0 5125,0 5126,0 5127,2 -5128,3 +5128,0 5129,0 5130,0 5131,1 @@ -5180,7 +5180,7 @@ Id,label 5178,2 5179,1 5180,3 -5181,0 +5181,3 5182,2 5183,1 5184,1 @@ -5202,7 +5202,7 @@ Id,label 5200,3 5201,0 5202,0 -5203,3 +5203,0 5204,3 5205,1 5206,2 @@ -5255,10 +5255,10 @@ Id,label 5253,3 5254,2 5255,0 -5256,0 +5256,2 5257,0 5258,0 -5259,0 +5259,3 5260,3 5261,2 5262,0 @@ -5309,7 +5309,7 @@ Id,label 5307,3 5308,0 5309,0 -5310,0 +5310,3 5311,2 5312,0 5313,0 @@ -5354,7 +5354,7 @@ Id,label 5352,0 5353,3 5354,3 -5355,0 +5355,3 5356,3 5357,2 5358,3 @@ -5453,7 +5453,7 @@ Id,label 5451,2 5452,3 5453,1 -5454,3 +5454,0 5455,0 5456,0 5457,0 @@ -5520,7 +5520,7 @@ Id,label 5518,2 5519,0 5520,0 -5521,3 +5521,0 5522,0 5523,3 5524,3 @@ -5547,7 +5547,7 @@ Id,label 5545,0 5546,0 5547,3 -5548,0 +5548,3 5549,0 5550,3 5551,0 @@ -5599,7 +5599,7 @@ Id,label 5597,0 5598,0 5599,1 -5600,3 +5600,0 5601,1 5602,2 5603,3 @@ -5632,7 +5632,7 @@ Id,label 5630,2 5631,2 5632,0 -5633,0 +5633,3 5634,2 5635,0 5636,0 @@ -5660,7 +5660,7 @@ Id,label 5658,3 5659,0 5660,0 -5661,1 +5661,0 5662,3 5663,2 5664,3 @@ -5700,7 +5700,7 @@ Id,label 5698,3 5699,0 5700,2 -5701,0 +5701,3 5702,2 5703,3 5704,0 @@ -5848,7 +5848,7 @@ Id,label 5846,0 5847,2 5848,1 -5849,0 +5849,3 5850,1 5851,1 5852,2 @@ -5958,7 +5958,7 @@ Id,label 5956,3 5957,1 5958,0 -5959,3 +5959,0 5960,3 5961,0 5962,2 @@ -5985,7 +5985,7 @@ Id,label 5983,3 5984,3 5985,2 -5986,0 +5986,3 5987,1 5988,2 5989,0 @@ -6035,7 +6035,7 @@ Id,label 6033,3 6034,0 6035,2 -6036,0 +6036,3 6037,0 6038,2 6039,0 @@ -6133,7 +6133,7 @@ Id,label 6131,0 6132,0 6133,0 -6134,0 +6134,3 6135,0 6136,0 6137,3 @@ -6238,7 +6238,7 @@ Id,label 6236,0 6237,0 6238,0 -6239,0 +6239,3 6240,0 6241,0 6242,0 @@ -6289,7 +6289,7 @@ Id,label 6287,3 6288,2 6289,1 -6290,0 +6290,3 6291,3 6292,0 6293,0 @@ -6315,13 +6315,13 @@ Id,label 6313,0 6314,3 6315,3 -6316,0 +6316,3 6317,2 6318,3 6319,3 6320,3 6321,3 -6322,0 +6322,3 6323,2 6324,2 6325,0 @@ -6364,7 +6364,7 @@ Id,label 6362,3 6363,2 6364,3 -6365,3 +6365,0 6366,2 6367,1 6368,0 @@ -6453,7 +6453,7 @@ Id,label 6451,0 6452,3 6453,0 -6454,0 +6454,3 6455,0 6456,0 6457,0 @@ -6541,7 +6541,7 @@ Id,label 6539,2 6540,0 6541,0 -6542,3 +6542,0 6543,0 6544,0 6545,1 @@ -6622,14 +6622,14 @@ Id,label 6620,0 6621,0 6622,3 -6623,0 +6623,3 6624,0 6625,1 6626,0 6627,3 6628,0 -6629,3 -6630,0 +6629,0 +6630,3 6631,3 6632,2 6633,2 @@ -6653,7 +6653,7 @@ Id,label 6651,0 6652,0 6653,3 -6654,0 +6654,3 6655,0 6656,0 6657,0 @@ -6827,7 +6827,7 @@ Id,label 6825,2 6826,2 6827,3 -6828,0 +6828,3 6829,0 6830,3 6831,0 @@ -6866,7 +6866,7 @@ Id,label 6864,0 6865,1 6866,2 -6867,0 +6867,3 6868,2 6869,1 6870,1 @@ -6929,7 +6929,7 @@ Id,label 6927,0 6928,2 6929,1 -6930,0 +6930,3 6931,1 6932,0 6933,3 @@ -7074,7 +7074,7 @@ Id,label 7072,2 7073,2 7074,2 -7075,0 +7075,3 7076,0 7077,0 7078,3 @@ -7108,7 +7108,7 @@ Id,label 7106,1 7107,2 7108,3 -7109,3 +7109,0 7110,0 7111,1 7112,2 @@ -7247,7 +7247,7 @@ Id,label 7245,3 7246,2 7247,2 -7248,0 +7248,3 7249,1 7250,0 7251,2 @@ -7282,12 +7282,12 @@ Id,label 7280,1 7281,3 7282,1 -7283,0 +7283,3 7284,3 7285,0 7286,1 7287,3 -7288,0 +7288,3 7289,1 7290,3 7291,0 @@ -7358,7 +7358,7 @@ Id,label 7356,1 7357,1 7358,0 -7359,0 +7359,3 7360,0 7361,0 7362,1 @@ -7419,12 +7419,12 @@ Id,label 7417,0 7418,2 7419,1 -7420,0 +7420,3 7421,0 7422,3 7423,3 7424,1 -7425,0 +7425,3 7426,0 7427,0 7428,3 @@ -7478,7 +7478,7 @@ Id,label 7476,1 7477,2 7478,0 -7479,3 +7479,0 7480,0 7481,0 7482,3 @@ -7525,7 +7525,7 @@ Id,label 7523,3 7524,0 7525,1 -7526,0 +7526,3 7527,3 7528,0 7529,0 @@ -7545,7 +7545,7 @@ Id,label 7543,0 7544,0 7545,2 -7546,0 +7546,3 7547,0 7548,3 7549,0 @@ -7554,8 +7554,8 @@ Id,label 7552,0 7553,2 7554,3 -7555,3 -7556,0 +7555,0 +7556,3 7557,0 7558,3 7559,1 @@ -7590,7 +7590,7 @@ Id,label 7588,0 7589,0 7590,0 -7591,0 +7591,3 7592,2 7593,0 7594,3 @@ -7633,7 +7633,7 @@ Id,label 7631,0 7632,1 7633,3 -7634,0 +7634,3 7635,1 7636,0 7637,1 @@ -7650,7 +7650,7 @@ Id,label 7648,2 7649,0 7650,1 -7651,0 +7651,3 7652,2 7653,1 7654,2 @@ -7658,7 +7658,7 @@ Id,label 7656,0 7657,3 7658,0 -7659,0 +7659,3 7660,2 7661,0 7662,0 @@ -7794,10 +7794,10 @@ Id,label 7792,2 7793,3 7794,0 -7795,3 +7795,0 7796,2 7797,2 -7798,0 +7798,3 7799,0 7800,0 7801,3 @@ -7810,7 +7810,7 @@ Id,label 7808,3 7809,0 7810,3 -7811,0 +7811,3 7812,2 7813,1 7814,0 @@ -7890,10 +7890,10 @@ Id,label 7888,2 7889,1 7890,0 -7891,0 +7891,3 7892,1 7893,2 -7894,0 +7894,3 7895,0 7896,0 7897,1 @@ -7907,7 +7907,7 @@ Id,label 7905,1 7906,2 7907,0 -7908,0 +7908,3 7909,3 7910,1 7911,2 @@ -8013,7 +8013,7 @@ Id,label 8011,3 8012,0 8013,3 -8014,3 +8014,0 8015,0 8016,3 8017,3 @@ -8110,7 +8110,7 @@ Id,label 8108,3 8109,2 8110,1 -8111,0 +8111,3 8112,0 8113,3 8114,2 @@ -8215,7 +8215,7 @@ Id,label 8213,0 8214,0 8215,0 -8216,0 +8216,3 8217,2 8218,1 8219,3 @@ -8229,7 +8229,7 @@ Id,label 8227,0 8228,0 8229,0 -8230,0 +8230,3 8231,0 8232,3 8233,0 @@ -8353,7 +8353,7 @@ Id,label 8351,3 8352,3 8353,2 -8354,0 +8354,3 8355,0 8356,2 8357,3 @@ -8392,12 +8392,12 @@ Id,label 8390,2 8391,1 8392,3 -8393,0 +8393,3 8394,0 8395,2 8396,3 8397,0 -8398,3 +8398,0 8399,2 8400,0 8401,1 @@ -8428,7 +8428,7 @@ Id,label 8426,0 8427,0 8428,0 -8429,0 +8429,2 8430,3 8431,1 8432,2 @@ -8504,7 +8504,7 @@ Id,label 8502,2 8503,2 8504,1 -8505,0 +8505,2 8506,0 8507,0 8508,1 @@ -8637,7 +8637,7 @@ Id,label 8635,2 8636,0 8637,3 -8638,0 +8638,3 8639,0 8640,0 8641,0 @@ -8675,7 +8675,7 @@ Id,label 8673,0 8674,0 8675,0 -8676,3 +8676,0 8677,1 8678,3 8679,1 @@ -8724,7 +8724,7 @@ Id,label 8722,2 8723,0 8724,3 -8725,0 +8725,3 8726,0 8727,0 8728,0 @@ -8806,7 +8806,7 @@ Id,label 8804,0 8805,3 8806,0 -8807,0 +8807,3 8808,0 8809,0 8810,1 @@ -9055,9 +9055,9 @@ Id,label 9053,0 9054,1 9055,3 -9056,0 +9056,3 9057,2 -9058,0 +9058,3 9059,2 9060,0 9061,2 @@ -9143,7 +9143,7 @@ Id,label 9141,2 9142,0 9143,3 -9144,3 +9144,0 9145,0 9146,2 9147,2 @@ -9239,7 +9239,7 @@ Id,label 9237,1 9238,3 9239,0 -9240,0 +9240,3 9241,2 9242,3 9243,1 @@ -9401,7 +9401,7 @@ Id,label 9399,0 9400,2 9401,0 -9402,0 +9402,3 9403,2 9404,0 9405,0 @@ -9424,12 +9424,12 @@ Id,label 9422,2 9423,2 9424,3 -9425,0 +9425,3 9426,0 9427,2 9428,0 9429,0 -9430,0 +9430,3 9431,2 9432,2 9433,0 @@ -9717,7 +9717,7 @@ Id,label 9715,0 9716,0 9717,2 -9718,0 +9718,3 9719,3 9720,2 9721,0 @@ -9830,7 +9830,7 @@ Id,label 9828,0 9829,0 9830,0 -9831,0 +9831,3 9832,0 9833,0 9834,3 @@ -9889,11 +9889,11 @@ Id,label 9887,2 9888,2 9889,3 -9890,0 +9890,3 9891,0 9892,0 9893,2 -9894,3 +9894,0 9895,0 9896,1 9897,0 @@ -10015,7 +10015,7 @@ Id,label 10013,0 10014,0 10015,2 -10016,0 +10016,3 10017,0 10018,3 10019,2 @@ -10197,7 +10197,7 @@ Id,label 10195,0 10196,2 10197,3 -10198,0 +10198,3 10199,2 10200,0 10201,2 @@ -10373,7 +10373,7 @@ Id,label 10371,0 10372,0 10373,3 -10374,0 +10374,3 10375,2 10376,0 10377,3 @@ -10539,7 +10539,7 @@ Id,label 10537,0 10538,1 10539,1 -10540,0 +10540,3 10541,2 10542,0 10543,1 @@ -10563,7 +10563,7 @@ Id,label 10561,0 10562,3 10563,1 -10564,0 +10564,3 10565,0 10566,0 10567,3 @@ -10630,7 +10630,7 @@ Id,label 10628,0 10629,0 10630,3 -10631,3 +10631,0 10632,0 10633,3 10634,1 @@ -10661,7 +10661,7 @@ Id,label 10659,1 10660,1 10661,1 -10662,0 +10662,3 10663,1 10664,2 10665,0 @@ -10706,7 +10706,7 @@ Id,label 10704,2 10705,1 10706,0 -10707,0 +10707,3 10708,3 10709,0 10710,0