Skip to content

Commit b93efc0

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 0849146 commit b93efc0

2 files changed

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

0 commit comments

Comments
 (0)