Text Classification
Transformers
Safetensors
code
roberta
clone-detection
graphcodebert
code-similarity
Eval Results (legacy)
text-embeddings-inference
Instructions to use thealper2/graphcodebert-code-clone-detection with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use thealper2/graphcodebert-code-clone-detection with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="thealper2/graphcodebert-code-clone-detection")# Load model directly from transformers import AutoTokenizer, GraphCodeBERTForCloneDetection tokenizer = AutoTokenizer.from_pretrained("thealper2/graphcodebert-code-clone-detection") model = GraphCodeBERTForCloneDetection.from_pretrained("thealper2/graphcodebert-code-clone-detection", device_map="auto") - Notebooks
- Google Colab
- Kaggle
| """Data-flow graph (DFG) extraction for GraphCodeBERT. | |
| This is a faithful port of Microsoft's GraphCodeBERT ``parser/`` package | |
| (``utils.py`` + the ``DFG_python`` extractor from ``DFG.py``), adapted to the | |
| modern ``py-tree-sitter`` API (>= 0.22, ``Language(tree_sitter_python.language())``) | |
| instead of the original hand-compiled ``my-languages.so``. | |
| The dataset used in this project contains **Python** snippets (verified in | |
| ``preprocess.py``), so only the Python extractor is ported; adding another | |
| language means adding its ``DFG_<lang>`` function and grammar package here. | |
| A DFG entry is the 5-tuple used throughout GraphCodeBERT:: | |
| (variable_name, token_index, edge_type, source_variable_names, source_token_indices) | |
| ``edge_type`` is ``"comesFrom"`` (value flows from a previous definition) or | |
| ``"computedFrom"`` (value is computed from the right-hand side of an assignment). | |
| """ | |
| from __future__ import annotations | |
| import io | |
| import re | |
| import sys | |
| import tokenize | |
| from typing import Any | |
| from tree_sitter import Language, Node, Parser | |
| __all__ = [ | |
| "get_parser", | |
| "extract_dataflow", | |
| "remove_comments_and_docstrings", | |
| "DataFlowExtractionError", | |
| ] | |
| #: tree-sitter recursion is mirrored by the recursive Python walkers below. | |
| #: Competitive-programming snippets can nest deeply, so raise the ceiling but | |
| #: keep it bounded so a pathological file raises RecursionError instead of | |
| #: segfaulting the worker. | |
| _RECURSION_LIMIT = 10_000 | |
| class DataFlowExtractionError(RuntimeError): | |
| """Raised when a snippet cannot be turned into code tokens at all.""" | |
| _PARSER_CACHE: dict[str, Parser] = {} | |
| def get_parser(language: str = "python") -> Parser: | |
| """Return a cached tree-sitter parser for ``language``. | |
| Cached per process so that ``datasets.map(num_proc=...)`` workers each build | |
| the parser once rather than once per snippet. | |
| """ | |
| if language in _PARSER_CACHE: | |
| return _PARSER_CACHE[language] | |
| if language != "python": | |
| raise ValueError( | |
| f"Only the Python grammar is wired up (requested {language!r}). " | |
| "Add the matching tree_sitter_<lang> package and DFG_<lang> function." | |
| ) | |
| try: | |
| import tree_sitter_python | |
| except ImportError as exc: # pragma: no cover - environment problem | |
| raise ImportError( | |
| "tree_sitter_python is required for GraphCodeBERT data-flow extraction. " | |
| "Install it with `pip install tree-sitter tree-sitter-python`." | |
| ) from exc | |
| parser = Parser(Language(tree_sitter_python.language())) | |
| _PARSER_CACHE[language] = parser | |
| return parser | |
| # --------------------------------------------------------------------------- # | |
| # parser/utils.py | |
| # --------------------------------------------------------------------------- # | |
| def remove_comments_and_docstrings(source: str, lang: str = "python") -> str: | |
| """Strip comments and docstrings, preserving token columns. | |
| Column positions are preserved because the DFG indices are ``(row, column)`` | |
| points into the *cleaned* source. | |
| """ | |
| if lang == "python": | |
| io_obj = io.StringIO(source) | |
| out = "" | |
| prev_toktype = tokenize.INDENT | |
| last_lineno = -1 | |
| last_col = 0 | |
| for tok in tokenize.generate_tokens(io_obj.readline): | |
| token_type, token_string = tok[0], tok[1] | |
| start_line, start_col = tok[2] | |
| end_line, end_col = tok[3] | |
| if start_line > last_lineno: | |
| last_col = 0 | |
| if start_col > last_col: | |
| out += " " * (start_col - last_col) | |
| if token_type == tokenize.COMMENT: | |
| pass | |
| elif token_type == tokenize.STRING: | |
| # A string that starts a logical line is a docstring -> drop it. | |
| if prev_toktype != tokenize.INDENT and prev_toktype != tokenize.NEWLINE: | |
| if start_col > 0: | |
| out += token_string | |
| else: | |
| out += token_string | |
| prev_toktype = token_type | |
| last_col = end_col | |
| last_lineno = end_line | |
| return "\n".join(x for x in out.split("\n") if x.strip() != "") | |
| def _replacer(match: re.Match[str]) -> str: | |
| s = match.group(0) | |
| return " " if s.startswith("/") else s | |
| pattern = re.compile( | |
| r"//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|\"(?:\\.|[^\\\"])*\"", | |
| re.DOTALL | re.MULTILINE, | |
| ) | |
| cleaned = re.sub(pattern, _replacer, source) | |
| return "\n".join(x for x in cleaned.split("\n") if x.strip() != "") | |
| def tree_to_token_index(root_node: Node) -> list[tuple[Any, Any]]: | |
| """Collect ``(start_point, end_point)`` spans of every leaf token.""" | |
| if (len(root_node.children) == 0 or root_node.type == "string") and root_node.type != "comment": | |
| return [(root_node.start_point, root_node.end_point)] | |
| spans: list[tuple[Any, Any]] = [] | |
| for child in root_node.children: | |
| spans += tree_to_token_index(child) | |
| return spans | |
| def tree_to_variable_index(root_node: Node, index_to_code: dict) -> list[tuple[Any, Any]]: | |
| """Collect spans of leaves that are *variables* (token text != node type).""" | |
| if (len(root_node.children) == 0 or root_node.type == "string") and root_node.type != "comment": | |
| index = (root_node.start_point, root_node.end_point) | |
| _, code = index_to_code[index] | |
| return [] if root_node.type == code else [index] | |
| spans: list[tuple[Any, Any]] = [] | |
| for child in root_node.children: | |
| spans += tree_to_variable_index(child, index_to_code) | |
| return spans | |
| def index_to_code_token(index: tuple[Any, Any], code: list[str]) -> str: | |
| """Slice the source text covered by a ``(start_point, end_point)`` span.""" | |
| start_point, end_point = index | |
| if start_point[0] == end_point[0]: | |
| return code[start_point[0]][start_point[1] : end_point[1]] | |
| s = code[start_point[0]][start_point[1] :] | |
| for i in range(start_point[0] + 1, end_point[0]): | |
| s += code[i] | |
| s += code[end_point[0]][: end_point[1]] | |
| return s | |
| # --------------------------------------------------------------------------- # | |
| # parser/DFG.py :: DFG_python | |
| # --------------------------------------------------------------------------- # | |
| _ASSIGNMENT = ("assignment", "augmented_assignment", "for_in_clause") | |
| _IF_STATEMENT = ("if_statement",) | |
| _FOR_STATEMENT = ("for_statement",) | |
| _WHILE_STATEMENT = ("while_statement",) | |
| _DO_FIRST_STATEMENT = ("for_in_clause",) | |
| _DEF_STATEMENT = ("default_parameter",) | |
| def DFG_python(root_node: Node, index_to_code: dict, states: dict) -> tuple[list, dict]: | |
| """Build the data-flow graph of a Python AST subtree. | |
| Returns ``(dfg_edges, variable_states)`` where ``variable_states`` maps a | |
| variable name to the token indices that currently define it. | |
| """ | |
| states = states.copy() | |
| if (len(root_node.children) == 0 or root_node.type == "string") and root_node.type != "comment": | |
| idx, code = index_to_code[(root_node.start_point, root_node.end_point)] | |
| if root_node.type == code: # a keyword/operator, not a variable | |
| return [], states | |
| if code in states: | |
| return [(code, idx, "comesFrom", [code], states[code].copy())], states | |
| if root_node.type == "identifier": | |
| states[code] = [idx] | |
| return [(code, idx, "comesFrom", [], [])], states | |
| if root_node.type in _DEF_STATEMENT: | |
| name = root_node.child_by_field_name("name") | |
| value = root_node.child_by_field_name("value") | |
| dfg: list = [] | |
| if value is None: | |
| for index in tree_to_variable_index(name, index_to_code): | |
| idx, code = index_to_code[index] | |
| dfg.append((code, idx, "comesFrom", [], [])) | |
| states[code] = [idx] | |
| return sorted(dfg, key=lambda x: x[1]), states | |
| name_indexs = tree_to_variable_index(name, index_to_code) | |
| value_indexs = tree_to_variable_index(value, index_to_code) | |
| temp, states = DFG_python(value, index_to_code, states) | |
| dfg += temp | |
| for index1 in name_indexs: | |
| idx1, code1 = index_to_code[index1] | |
| for index2 in value_indexs: | |
| idx2, code2 = index_to_code[index2] | |
| dfg.append((code1, idx1, "comesFrom", [code2], [idx2])) | |
| states[code1] = [idx1] | |
| return sorted(dfg, key=lambda x: x[1]), states | |
| if root_node.type in _ASSIGNMENT: | |
| if root_node.type == "for_in_clause": | |
| right_nodes = [root_node.children[-1]] | |
| left_nodes = [root_node.child_by_field_name("left")] | |
| else: | |
| if root_node.child_by_field_name("right") is None: | |
| return [], states | |
| left_nodes = [x for x in root_node.child_by_field_name("left").children if x.type != ","] | |
| right_nodes = [ | |
| x for x in root_node.child_by_field_name("right").children if x.type != "," | |
| ] | |
| if len(right_nodes) != len(left_nodes): | |
| left_nodes = [root_node.child_by_field_name("left")] | |
| right_nodes = [root_node.child_by_field_name("right")] | |
| if len(left_nodes) == 0: | |
| left_nodes = [root_node.child_by_field_name("left")] | |
| if len(right_nodes) == 0: | |
| right_nodes = [root_node.child_by_field_name("right")] | |
| dfg = [] | |
| for node in right_nodes: | |
| temp, states = DFG_python(node, index_to_code, states) | |
| dfg += temp | |
| for left_node, right_node in zip(left_nodes, right_nodes): | |
| left_tokens_index = tree_to_variable_index(left_node, index_to_code) | |
| right_tokens_index = tree_to_variable_index(right_node, index_to_code) | |
| for token1_index in left_tokens_index: | |
| idx1, code1 = index_to_code[token1_index] | |
| dfg.append( | |
| ( | |
| code1, | |
| idx1, | |
| "computedFrom", | |
| [index_to_code[x][1] for x in right_tokens_index], | |
| [index_to_code[x][0] for x in right_tokens_index], | |
| ) | |
| ) | |
| states[code1] = [idx1] | |
| return sorted(dfg, key=lambda x: x[1]), states | |
| if root_node.type in _IF_STATEMENT: | |
| dfg = [] | |
| current_states = states.copy() | |
| others_states = [] | |
| tag = "else" in root_node.type | |
| for child in root_node.children: | |
| if "else" in child.type: | |
| tag = True | |
| if child.type not in ("elif_clause", "else_clause"): | |
| temp, current_states = DFG_python(child, index_to_code, current_states) | |
| dfg += temp | |
| else: | |
| temp, new_states = DFG_python(child, index_to_code, states) | |
| dfg += temp | |
| others_states.append(new_states) | |
| others_states.append(current_states) | |
| if tag is False: | |
| others_states.append(states) | |
| merged: dict = {} | |
| for dic in others_states: | |
| for key in dic: | |
| merged.setdefault(key, []) | |
| merged[key] += dic[key] | |
| for key in merged: | |
| merged[key] = sorted(set(merged[key])) | |
| return sorted(dfg, key=lambda x: x[1]), merged | |
| if root_node.type in _FOR_STATEMENT: | |
| dfg = [] | |
| # Two passes: loop bodies can consume values defined later in the loop. | |
| for _ in range(2): | |
| right_nodes = [x for x in root_node.child_by_field_name("right").children if x.type != ","] | |
| left_nodes = [x for x in root_node.child_by_field_name("left").children if x.type != ","] | |
| if len(right_nodes) != len(left_nodes): | |
| left_nodes = [root_node.child_by_field_name("left")] | |
| right_nodes = [root_node.child_by_field_name("right")] | |
| if len(left_nodes) == 0: | |
| left_nodes = [root_node.child_by_field_name("left")] | |
| if len(right_nodes) == 0: | |
| right_nodes = [root_node.child_by_field_name("right")] | |
| for node in right_nodes: | |
| temp, states = DFG_python(node, index_to_code, states) | |
| dfg += temp | |
| for left_node, right_node in zip(left_nodes, right_nodes): | |
| left_tokens_index = tree_to_variable_index(left_node, index_to_code) | |
| right_tokens_index = tree_to_variable_index(right_node, index_to_code) | |
| for token1_index in left_tokens_index: | |
| idx1, code1 = index_to_code[token1_index] | |
| dfg.append( | |
| ( | |
| code1, | |
| idx1, | |
| "computedFrom", | |
| [index_to_code[x][1] for x in right_tokens_index], | |
| [index_to_code[x][0] for x in right_tokens_index], | |
| ) | |
| ) | |
| states[code1] = [idx1] | |
| if root_node.children[-1].type == "block": | |
| temp, states = DFG_python(root_node.children[-1], index_to_code, states) | |
| dfg += temp | |
| return _merge_duplicate_edges(dfg), states | |
| if root_node.type in _WHILE_STATEMENT: | |
| dfg = [] | |
| for _ in range(2): | |
| for child in root_node.children: | |
| temp, states = DFG_python(child, index_to_code, states) | |
| dfg += temp | |
| return _merge_duplicate_edges(dfg), states | |
| dfg = [] | |
| for child in root_node.children: | |
| if child.type in _DO_FIRST_STATEMENT: | |
| temp, states = DFG_python(child, index_to_code, states) | |
| dfg += temp | |
| for child in root_node.children: | |
| if child.type not in _DO_FIRST_STATEMENT: | |
| temp, states = DFG_python(child, index_to_code, states) | |
| dfg += temp | |
| return sorted(dfg, key=lambda x: x[1]), states | |
| def _merge_duplicate_edges(dfg: list) -> list: | |
| """Collapse the duplicate edges produced by the two-pass loop handling.""" | |
| dic: dict = {} | |
| for x in dfg: | |
| key = (x[0], x[1], x[2]) | |
| if key not in dic: | |
| dic[key] = [x[3], x[4]] | |
| else: | |
| dic[key][0] = list(set(dic[key][0] + x[3])) | |
| dic[key][1] = sorted(set(dic[key][1] + x[4])) | |
| merged = [(k[0], k[1], k[2], v[0], v[1]) for k, v in sorted(dic.items(), key=lambda t: t[0][1])] | |
| return sorted(merged, key=lambda x: x[1]) | |
| # --------------------------------------------------------------------------- # | |
| # Public entry point (GraphCodeBERT's `extract_dataflow`) | |
| # --------------------------------------------------------------------------- # | |
| def extract_dataflow(code: str, language: str = "python") -> tuple[list[str], list, dict]: | |
| """Tokenise ``code`` and extract its data-flow graph. | |
| Returns ``(code_tokens, dfg, status)``. ``status`` records *why* a stage | |
| degraded so callers can report it instead of hiding it: | |
| ``comment_strip`` : ``"ok"`` | ``"failed"`` | |
| ``parse`` : ``"ok"`` | ``"failed"`` | |
| ``dfg`` : ``"ok"`` | ``"failed"`` | ``"recursion_limit"`` | |
| ``error`` : ``None`` or ``"<ExcType>: <message>"`` | |
| A degraded DFG yields an **empty** data-flow component -- the snippet is | |
| still trained on (GraphCodeBERT tolerates zero nodes), it is never dropped. | |
| """ | |
| status: dict[str, Any] = {"comment_strip": "ok", "parse": "ok", "dfg": "ok", "error": None} | |
| try: | |
| cleaned = remove_comments_and_docstrings(code, language) | |
| except Exception as exc: | |
| # Syntactically broken snippets are common in the wild; fall back to the | |
| # raw source rather than discarding the example. | |
| status["comment_strip"] = "failed" | |
| status["error"] = f"{type(exc).__name__}: {exc}" | |
| cleaned = code | |
| parser = get_parser(language) | |
| try: | |
| tree = parser.parse(bytes(cleaned, "utf8")) | |
| root_node = tree.root_node | |
| except Exception as exc: | |
| raise DataFlowExtractionError(f"tree-sitter failed to parse snippet: {exc}") from exc | |
| old_limit = sys.getrecursionlimit() | |
| sys.setrecursionlimit(_RECURSION_LIMIT) | |
| try: | |
| try: | |
| tokens_index = tree_to_token_index(root_node) | |
| except RecursionError as exc: | |
| status["parse"] = "failed" | |
| status["dfg"] = "recursion_limit" | |
| status["error"] = f"{type(exc).__name__}: token index recursion limit" | |
| raise DataFlowExtractionError("snippet nests deeper than the recursion limit") from exc | |
| lines = cleaned.split("\n") | |
| code_tokens = [index_to_code_token(x, lines) for x in tokens_index] | |
| index_to_code = { | |
| index: (idx, token) for idx, (index, token) in enumerate(zip(tokens_index, code_tokens)) | |
| } | |
| try: | |
| dfg, _ = DFG_python(root_node, index_to_code, {}) | |
| except RecursionError as exc: | |
| status["dfg"] = "recursion_limit" | |
| status["error"] = f"{type(exc).__name__}: DFG recursion limit" | |
| dfg = [] | |
| except Exception as exc: | |
| status["dfg"] = "failed" | |
| status["error"] = f"{type(exc).__name__}: {exc}" | |
| dfg = [] | |
| finally: | |
| sys.setrecursionlimit(old_limit) | |
| # Keep only nodes that participate in at least one edge (GraphCodeBERT does | |
| # the same: isolated nodes carry no data-flow signal). | |
| dfg = sorted(dfg, key=lambda x: x[1]) | |
| keep: set[int] = set() | |
| for d in dfg: | |
| if len(d[-1]) != 0: | |
| keep.add(d[1]) | |
| keep.update(d[-1]) | |
| dfg = [d for d in dfg if d[1] in keep] | |
| return code_tokens, dfg, status | |