Skip to content

Commit 769c00e

Browse files
cortinicometa-codesync[bot]
authored andcommitted
Add Python to yarn format
Summary: Add `yarn format-python` and `yarn format-check-python` using Ruff, and compose them into the repository-wide commands. The wrapper selects a repository-provided Ruff binary when available or bootstraps pinned Ruff through Python 3 and pip. Missing tools produce environment-specific setup guidance before Python is skipped. Changelog: [Internal] Differential Revision: D119487614
1 parent 5282058 commit 769c00e

2 files changed

Lines changed: 207 additions & 2 deletions

File tree

‎package.json‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,18 @@
1414
"cxx-api-validate": "python -m scripts.cxx-api.parser --validate",
1515
"flow-check": "flow full-check",
1616
"flow": "flow",
17-
"format-check": "yarn format-check-javascript && yarn format-check-cpp && yarn format-check-kotlin && yarn format-check-java",
17+
"format-check": "yarn format-check-javascript && yarn format-check-cpp && yarn format-check-kotlin && yarn format-check-java && yarn format-check-python",
1818
"format-check-cpp": "node ./scripts/clang-format.js --check",
1919
"format-check-java": "node ./scripts/format-java.js --check",
2020
"format-check-javascript": "prettier --check \"./**/*.{cjs,cts,flow,js,jsx,md,mjs,mts,ts,tsx,yaml,yml}\"",
2121
"format-check-kotlin": "node ./scripts/format-kotlin.js --check",
22-
"format": "yarn format-javascript && yarn format-cpp && yarn format-kotlin && yarn format-java",
22+
"format-check-python": "node ./scripts/format-python.js --check",
23+
"format": "yarn format-javascript && yarn format-cpp && yarn format-kotlin && yarn format-java && yarn format-python",
2324
"format-cpp": "node ./scripts/clang-format.js",
2425
"format-java": "node ./scripts/format-java.js",
2526
"format-javascript": "prettier --write \"./**/*.{cjs,cts,flow,js,jsx,md,mjs,mts,ts,tsx,yaml,yml}\"",
2627
"format-kotlin": "node ./scripts/format-kotlin.js",
28+
"format-python": "node ./scripts/format-python.js",
2729
"featureflags": "yarn --cwd packages/react-native featureflags",
2830
"js-api-diff": "node ./scripts/js-api/diff-api-snapshot",
2931
"lint-markdown": "markdownlint-cli2 2>&1",

‎scripts/format-python.js‎

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @noflow
8+
* @format
9+
*/
10+
11+
'use strict';
12+
13+
const {IS_META_CHECKOUT, findMetaTool} = require('./format-utils');
14+
const {spawnSync} = require('node:child_process');
15+
const fs = require('node:fs');
16+
const path = require('node:path');
17+
18+
const REPO_ROOT = path.resolve(__dirname, '..');
19+
const RUFF_VERSION = '0.14.0';
20+
const RUFF_ROOT = path.join(
21+
REPO_ROOT,
22+
'node_modules',
23+
'.cache',
24+
'react-native-format',
25+
`ruff-${RUFF_VERSION}`,
26+
);
27+
28+
function run(command, args, options = {}) {
29+
const result = spawnSync(command, args, {
30+
cwd: REPO_ROOT,
31+
stdio: options.quiet === true ? 'ignore' : 'inherit',
32+
...options,
33+
});
34+
if (result.error != null) {
35+
if (options.quiet !== true) {
36+
console.error(result.error.message);
37+
}
38+
return {status: 1};
39+
}
40+
if (result.signal != null) {
41+
process.kill(process.pid, result.signal);
42+
return {status: 1};
43+
}
44+
return {status: result.status ?? 1};
45+
}
46+
47+
function findPython() {
48+
const candidates =
49+
process.platform === 'win32'
50+
? [
51+
['py', ['-3']],
52+
['python', []],
53+
]
54+
: [
55+
['python3', []],
56+
['python', []],
57+
];
58+
59+
for (const [command, prefixArguments] of candidates) {
60+
if (
61+
run(
62+
command,
63+
[
64+
...prefixArguments,
65+
'-c',
66+
'import sys; raise SystemExit(sys.version_info.major != 3)',
67+
],
68+
{quiet: true},
69+
).status === 0
70+
) {
71+
return {command, prefixArguments};
72+
}
73+
}
74+
return null;
75+
}
76+
77+
function warnMissingPython() {
78+
console.warn(
79+
'warning: Skipping Python formatting because Python 3 with pip was not found.\n' +
80+
'Please install Python 3 with pip and make sure `python3` (`py -3` on Windows) and pip are available in your PATH.',
81+
);
82+
}
83+
84+
function warnMissingMetaRuff() {
85+
console.warn(
86+
'warning: Skipping Python formatting because the Meta-managed Ruff tool could not run.\n' +
87+
'From the fbsource root, run `tools/third-party/ruff/ruff --version`. ' +
88+
'If that fails, repair your Meta DotSlash setup.',
89+
);
90+
}
91+
92+
function runRuff(command, prefixArguments, check) {
93+
if (
94+
run(command, [...prefixArguments, '--version'], {quiet: true}).status !== 0
95+
) {
96+
return false;
97+
}
98+
const format = run(command, [
99+
...prefixArguments,
100+
'format',
101+
...(check ? ['--check'] : []),
102+
'.',
103+
]);
104+
process.exit(format.status);
105+
}
106+
107+
function main() {
108+
const check = process.argv[2] === '--check';
109+
if (process.env.RUFF != null) {
110+
if (!runRuff(process.env.RUFF, [], check)) {
111+
if (IS_META_CHECKOUT) {
112+
warnMissingMetaRuff();
113+
} else {
114+
console.warn(
115+
'warning: Skipping Python formatting because the configured Ruff command could not run.\n' +
116+
'Please install Ruff and set RUFF=/path/to/ruff, or unset RUFF to use automatic installation.',
117+
);
118+
}
119+
return;
120+
}
121+
}
122+
123+
const metaRuff = findMetaTool('tools', 'third-party', 'ruff', 'ruff');
124+
if (metaRuff != null) {
125+
if (!runRuff(metaRuff.command, metaRuff.prefixArguments, check)) {
126+
warnMissingMetaRuff();
127+
}
128+
return;
129+
}
130+
if (IS_META_CHECKOUT) {
131+
warnMissingMetaRuff();
132+
return;
133+
}
134+
135+
const python = findPython();
136+
if (python == null) {
137+
warnMissingPython();
138+
return;
139+
}
140+
const pythonPath = [RUFF_ROOT, process.env.PYTHONPATH]
141+
.filter(Boolean)
142+
.join(path.delimiter);
143+
const environment = {...process.env, PYTHONPATH: pythonPath};
144+
145+
if (
146+
run(python.command, [...python.prefixArguments, '-c', 'import ruff'], {
147+
env: environment,
148+
quiet: true,
149+
}).status !== 0
150+
) {
151+
if (
152+
run(
153+
python.command,
154+
[...python.prefixArguments, '-m', 'pip', '--version'],
155+
{quiet: true},
156+
).status !== 0
157+
) {
158+
warnMissingPython();
159+
return;
160+
}
161+
try {
162+
fs.mkdirSync(RUFF_ROOT, {recursive: true});
163+
} catch (error) {
164+
console.warn(
165+
`warning: Skipping Python formatting because the Ruff cache could not be created: ${String(error)}`,
166+
);
167+
return;
168+
}
169+
const install = run(python.command, [
170+
...python.prefixArguments,
171+
'-m',
172+
'pip',
173+
'install',
174+
'--disable-pip-version-check',
175+
'--only-binary=:all:',
176+
`--target=${RUFF_ROOT}`,
177+
`ruff==${RUFF_VERSION}`,
178+
]);
179+
if (install.status !== 0) {
180+
console.warn(
181+
`warning: Skipping Python formatting because Ruff ${RUFF_VERSION} could not be installed.\n` +
182+
'Please check your network connection, or install Ruff and set RUFF=/path/to/ruff.',
183+
);
184+
return;
185+
}
186+
}
187+
188+
const format = run(
189+
python.command,
190+
[
191+
...python.prefixArguments,
192+
'-m',
193+
'ruff',
194+
'format',
195+
...(check ? ['--check'] : []),
196+
'.',
197+
],
198+
{env: environment},
199+
);
200+
process.exit(format.status);
201+
}
202+
203+
main();

0 commit comments

Comments
 (0)